Public release of the DLU server code!

Have fun!
This commit is contained in:
Unknown
2021-12-05 18:54:36 +01:00
parent 5f7270e4ad
commit 0545adfac3
1146 changed files with 368646 additions and 1 deletions

View File

@@ -0,0 +1,323 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
// No longer used as I no longer support IO Completion ports
/*
#ifdef __USE_IO_COMPLETION_PORTS
#include "AsynchronousFileIO.h"
#include "ClientContextStruct.h"
#include <process.h>
#include "ExtendedOverlappedPool.h"
#include <stdio.h>
#include <assert.h>
// All these are used for the Read callback. For general Asynch file IO you would change these
#include "RakNetTypes.h"
class RakPeer;
#ifdef _WIN32
extern void __stdcall ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer );
#else
extern void ProcessNetworkPacket( unsigned int binaryAddress, unsigned short port, const char *data, int length, RakPeer *rakPeer );
#endif
AsynchronousFileIO AsynchronousFileIO::I;
AsynchronousFileIO::AsynchronousFileIO()
{
userCount = 0;
threadCount = 0;
completionPort = NULL;
// Determine how many processors are on the system.
GetSystemInfo( &systemInfo );
}
void AsynchronousFileIO::IncreaseUserCount()
{
userCountMutex.Lock();
++userCount;
if ( userCount == 1 )
{
// Create the completion port that will be used by all the worker
// threads.
completionPort = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, 0, systemInfo.dwNumberOfProcessors * 2 );
if ( completionPort == NULL )
{
userCount = 0;
userCountMutex.Unlock();
return ;
}
UINT nThreadID;
HANDLE workerHandle;
// Create worker threads
// One worker thread per processor
for ( DWORD i = 0; i < systemInfo.dwNumberOfProcessors * 2; i++ )
// In debug just make one worker thread so it's easier to trace
//for ( i = 0; i < systemInfo.dwNumberOfProcessors * 1; i++ )
{
workerHandle = ( HANDLE ) _beginthreadex( NULL, // Security
0, // Stack size - use default
ThreadPoolFunc, // Thread fn entry point
( void* ) completionPort, // Param for thread
0, // Init flag
&nThreadID ); // Thread address
// Feel free to comment this out for regular thread priority
SetThreadPriority( workerHandle, THREAD_PRIORITY_HIGHEST );
CloseHandle( workerHandle );
}
// Wait for the threads to start
while ( threadCount < systemInfo.dwNumberOfProcessors * 2 )
Sleep( 0 );
}
userCountMutex.Unlock();
}
void AsynchronousFileIO::DecreaseUserCount()
{
userCountMutex.Lock();
assert( userCount > 0 );
if ( userCount == 0 )
return ;
userCount--;
if ( userCount == 0 )
Shutdown();
userCountMutex.Unlock();
}
void AsynchronousFileIO::Shutdown( void )
{
killThreads = true;
if ( completionPort != NULL )
for ( DWORD i = 0; i < systemInfo.dwNumberOfProcessors * 2; i++ )
PostQueuedCompletionStatus( completionPort, 0, 0 , 0 );
// Kill worker threads
while ( threadCount > 0 )
Sleep( 0 );
if ( completionPort != NULL )
CloseHandle( completionPort );
}
int AsynchronousFileIO::GetUserCount( void )
{
return userCount;
}
AsynchronousFileIO::~AsynchronousFileIO()
{
if ( threadCount > 0 )
Shutdown();
}
bool AsynchronousFileIO::AssociateSocketWithCompletionPort( SOCKET socket, DWORD dwCompletionKey )
{
HANDLE h = CreateIoCompletionPort( ( HANDLE ) socket, completionPort, dwCompletionKey, 0 );
return h == completionPort;
}
BOOL ReadAsynch( HANDLE handle, ExtendedOverlappedStruct *extended )
{
BOOL success;
extended->read = true;
success = ReadFile( handle, extended->data, extended->length, 0, ( LPOVERLAPPED ) extended );
if ( !success )
{
DWORD dwErrCode = GetLastError();
if ( dwErrCode != ERROR_IO_PENDING )
{
#if defined(_WIN32) && !defined(_XBOX360) && defined(_DEBUG)
LPVOID messageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwErrCode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
( LPTSTR ) & messageBuffer, 0, NULL );
// something has gone wrong here...
printf( "ReadFile failed:Error code - %d\n%s", dwErrCode, messageBuffer );
//Free the buffer.
LocalFree( messageBuffer );
#endif
return FALSE;
}
}
return TRUE;
}
void WriteAsynch( HANDLE handle, ExtendedOverlappedStruct *extended )
{
//printf("Beginning asynch write of %i bytes.\n",extended->length);
//for (int i=0; i < extended->length && i < 10; i++)
// printf("%i ", extended->data[i]);
//printf("\n\n");
BOOL success;
extended->read = false;
success = WriteFile( handle, extended->data, extended->length, 0, ( LPOVERLAPPED ) extended );
if ( !success )
{
DWORD dwErrCode = GetLastError();
if ( dwErrCode != ERROR_IO_PENDING )
{
#if defined(_WIN32) && !defined(_XBOX360) && defined(_DEBUG)
LPVOID messageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwErrCode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
( LPTSTR ) & messageBuffer, 0, NULL );
// something has gone wrong here...
printf( "WriteFile failed:Error code - %d\n%s", dwErrCode, messageBuffer );
//Free the buffer.
LocalFree( messageBuffer );
#endif
}
}
}
unsigned __stdcall ThreadPoolFunc( LPVOID arguments )
{
DWORD dwIoSize;
ClientContextStruct* lpClientContext;
ExtendedOverlappedStruct* lpOverlapped;
LPOVERLAPPED temp;
BOOL bError;
HANDLE *completionPort = ( HANDLE * ) arguments;
AsynchronousFileIO::Instance()->threadCount++;
while ( 1 )
{
// Get a completed IO request.
BOOL returnValue = GetQueuedCompletionStatus(
completionPort,
&dwIoSize,
( LPDWORD ) & lpClientContext,
&temp, INFINITE );
lpOverlapped = ( ExtendedOverlappedStruct* ) temp;
DWORD dwIOError = GetLastError();
if ( lpOverlapped == 0 )
break; // Cancelled thread
if ( !returnValue && dwIOError != WAIT_TIMEOUT )
{
if ( dwIOError != ERROR_OPERATION_ABORTED )
{
// Print all but this very common error message
#if defined(_WIN32) && !defined(_XBOX360) && defined(_DEBUG)
LPVOID messageBuffer;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dwIOError, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
( LPTSTR ) & messageBuffer, 0, NULL );
// something has gone wrong here...
printf( "GetQueuedCompletionStatus failed:Error code - %d\n%s", dwIOError, messageBuffer );
//Free the buffer.
LocalFree( messageBuffer );
#endif
}
HANDLE_ERROR:
// Some kind of error. Erase the data for this call
bError = true;
// This socket is no longer used
if ( lpOverlapped )
delete lpOverlapped;
if ( lpClientContext )
delete lpClientContext;
// If we are killing the threads, then we keep posting fake completion statuses until we get a fake one through the queue (i.e. lpOverlapped==0 as above)
// This way we delete all the data from the real calls before exiting the thread
if ( AsynchronousFileIO::Instance()->killThreads )
{
PostQueuedCompletionStatus( completionPort, 0, 0, 0 );
}
}
else
bError = false;
if ( !bError )
{
if ( returnValue && NULL != lpOverlapped && NULL != lpClientContext )
{
if ( lpOverlapped->read == true )
{
assert( dwIoSize > 0 );
ProcessNetworkPacket( lpOverlapped->binaryAddress, lpOverlapped->port, lpOverlapped->data, dwIoSize, lpOverlapped->rakPeer );
// Issue a new read so we always have one outstanding read per socket
// Finished a read. Reuse the overlapped pointer
bError = ReadAsynch( lpClientContext->handle, lpOverlapped );
if ( !bError )
goto HANDLE_ERROR; // Windows is super unreliable!
}
else
{
// AsynchronousFileIO::Instance()->Write(lpClientContext);
// Finished a write
ExtendedOverlappedPool::Instance()->ReleasePointer( lpOverlapped );
}
}
else
assert( 0 );
}
}
AsynchronousFileIO::Instance()->threadCount--;
return 0;
}
#endif
*/

View File

@@ -0,0 +1,91 @@
/// \file
/// \brief \b [Internal] Depreciated, used for windows back when I supported IO completion ports.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
// No longer used as I no longer support IO Completion ports
/*
#ifdef __USE_IO_COMPLETION_PORTS
#ifndef __ASYNCHRONOUS_FILE_IO_H
#define __ASYNCHRONOUS_FILE_IO_H
#ifdef _XBOX360
#elif defined(_WIN32)
// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib
// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly
//#include <winsock2.h>
//#include <windows.h>
#endif
#include "SimpleMutex.h"
struct ExtendedOverlappedStruct;
/// Provides asynch file input and ouput, either for sockets or files
class AsynchronousFileIO
{
public:
/// Default Constructor
AsynchronousFileIO();
/// Destructor
~AsynchronousFileIO();
/// Associate a socket with a completion port
/// \param[in] socket the socket used for communication
/// \param[in] dwCompletionKey the completion port key
bool AssociateSocketWithCompletionPort( SOCKET socket, DWORD dwCompletionKey );if
/// Singleton instance
static inline AsynchronousFileIO* Instance()
{
return & I;
}
/// Increase the number of users of this instance
void IncreaseUserCount( void );
/// Decrease the number of users of this instance
void DecreaseUserCount( void );
/// Stop using asynchronous IO
void Shutdown( void );
/// Get the number of user of the instance
int GetUserCount( void );
unsigned threadCount;
bool killThreads;
private:
HANDLE completionPort;
SimpleMutex userCountMutex;
SYSTEM_INFO systemInfo;
int userCount;
static AsynchronousFileIO I;
};
unsigned __stdcall ThreadPoolFunc( LPVOID arguments );
void WriteAsynch( HANDLE handle, ExtendedOverlappedStruct *extended );
BOOL ReadAsynch( HANDLE handle, ExtendedOverlappedStruct *extended );
#endif
*/

717
thirdparty/raknet/Source/AutoRPC.cpp vendored Normal file
View File

@@ -0,0 +1,717 @@
#include "AutoRPC.h"
#include "RakMemoryOverride.h"
#include "RakAssert.h"
#include "StringCompressor.h"
#include "BitStream.h"
#include "Types.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include "NetworkIDObject.h"
#include "NetworkIDManager.h"
#include <stdlib.h>
using namespace RakNet;
#ifdef _MSC_VER
#pragma warning( push )
#endif
int AutoRPC::RemoteRPCFunctionComp( const RPCIdentifier &key, const RemoteRPCFunction &data )
{
if (key.isObjectMember==false && data.identifier.isObjectMember==true)
return -1;
if (key.isObjectMember==true && data.identifier.isObjectMember==false)
return 1;
return strcmp(key.uniqueIdentifier, data.identifier.uniqueIdentifier);
}
AutoRPC::AutoRPC()
{
currentExecution[0]=0;
rakPeer=0;
networkIdManager=0;
outgoingTimestamp=0;
outgoingPriority=HIGH_PRIORITY;
outgoingReliability=RELIABLE_ORDERED;
outgoingOrderingChannel=0;
outgoingBroadcast=true;
incomingTimeStamp=0;
DataStructures::Map<SystemAddress, DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *>::IMPLEMENT_DEFAULT_COMPARISON();
}
AutoRPC::~AutoRPC()
{
Clear();
}
void AutoRPC::SetNetworkIDManager(NetworkIDManager *idMan)
{
networkIdManager=idMan;
}
bool AutoRPC::RegisterFunction(const char *uniqueIdentifier, void* functionPtr, bool isObjectMember, char parameterCount)
{
return RegisterFunction( uniqueIdentifier, GenRPC::PMF( functionPtr ), isObjectMember, parameterCount );
}
bool AutoRPC::RegisterFunction(const char *uniqueIdentifier, GenRPC::PMF functionPtr, bool isObjectMember, char parameterCount)
{
if (uniqueIdentifier==0 || functionPtr==0)
{
RakAssert(0);
return false;
}
RPCIdentifier identifier;
identifier.isObjectMember=isObjectMember;
identifier.uniqueIdentifier=(char*) uniqueIdentifier;
unsigned localIndex = GetLocalFunctionIndex(identifier);
// Already registered?
if (localIndex!=(unsigned)-1 && localFunctions[localIndex].functionPtr!=0)
return false;
if (localIndex!=(unsigned)-1)
{
// Reenable existing
localFunctions[localIndex].functionPtr=functionPtr;
localFunctions[localIndex].parameterCount=parameterCount;
}
else
{
// Add new
LocalRPCFunction func;
func.functionPtr=functionPtr;
func.identifier.isObjectMember=isObjectMember;
func.identifier.uniqueIdentifier = (char*) rakMalloc(strlen(uniqueIdentifier)+1);
func.parameterCount=parameterCount;
strcpy(func.identifier.uniqueIdentifier, uniqueIdentifier);
localFunctions.Insert(func);
}
return true;
}
bool AutoRPC::UnregisterFunction(const char *uniqueIdentifier, bool isObjectMember)
{
if (uniqueIdentifier==0)
{
RakAssert(0);
return false;
}
RPCIdentifier identifier;
identifier.isObjectMember=isObjectMember;
identifier.uniqueIdentifier=(char*) uniqueIdentifier;
unsigned localIndex = GetLocalFunctionIndex(identifier);
// Not registered?
if (localIndex==(unsigned)-1)
return false;
// Leave the id in, in case the function is set again later. That way we keep the same remote index
localFunctions[localIndex].functionPtr=0;
return true;
}
void AutoRPC::SetTimestamp(RakNetTime timeStamp)
{
outgoingTimestamp=timeStamp;
}
void AutoRPC::SetSendParams(PacketPriority priority, PacketReliability reliability, char orderingChannel)
{
outgoingPriority=priority;
outgoingReliability=reliability;
outgoingOrderingChannel=orderingChannel;
}
void AutoRPC::SetRecipientAddress(SystemAddress systemAddress, bool broadcast)
{
outgoingSystemAddress=systemAddress;
outgoingBroadcast=broadcast;
}
void AutoRPC::SetRecipientObject(NetworkID networkID)
{
outgoingNetworkID=networkID;
}
RakNet::BitStream *AutoRPC::SetOutgoingExtraData(void)
{
return &outgoingExtraData;
}
RakNetTime AutoRPC::GetLastSenderTimestamp(void) const
{
return incomingTimeStamp;
}
SystemAddress AutoRPC::GetLastSenderAddress(void) const
{
return incomingSystemAddress;
}
RakPeerInterface *AutoRPC::GetRakPeer(void) const
{
return rakPeer;
}
const char *AutoRPC::GetCurrentExecution(void) const
{
return (const char *) currentExecution;
}
RakNet::BitStream *AutoRPC::GetIncomingExtraData(void)
{
return &incomingExtraData;
}
bool AutoRPC::SendCall(const char *uniqueIdentifier, const char *stack, unsigned int bytesOnStack, char parameterCount)
{
SystemAddress systemAddr;
RPCIdentifier identifier;
unsigned int outerIndex;
unsigned int innerIndex;
if (uniqueIdentifier==0)
return false;
identifier.uniqueIdentifier=(char*) uniqueIdentifier;
identifier.isObjectMember=(outgoingNetworkID!=UNASSIGNED_NETWORK_ID);
RakNet::BitStream bs;
if (outgoingTimestamp!=0)
{
bs.Write((MessageID)ID_TIMESTAMP);
bs.Write(outgoingTimestamp);
}
bs.Write((MessageID)ID_AUTO_RPC_CALL);
if (parameterCount>=0)
{
bs.Write(true);
bs.Write(parameterCount);
}
else
{
bs.Write(false);
}
bs.WriteCompressed(outgoingExtraData.GetNumberOfBitsUsed());
bs.Write(&outgoingExtraData);
if (outgoingNetworkID!=UNASSIGNED_NETWORK_ID)
{
bs.Write(true);
bs.Write(outgoingNetworkID);
}
else
{
bs.Write(false);
}
// This is so the call SetWriteOffset works
bs.AlignWriteToByteBoundary();
BitSize_t writeOffset = bs.GetWriteOffset();
if (outgoingBroadcast)
{
unsigned systemIndex;
for (systemIndex=0; systemIndex < rakPeer->GetMaximumNumberOfPeers(); systemIndex++)
{
systemAddr=rakPeer->GetSystemAddressFromIndex(systemIndex);
if (systemAddr!=UNASSIGNED_SYSTEM_ADDRESS && systemAddr!=outgoingSystemAddress)
{
if (GetRemoteFunctionIndex(systemAddr, identifier, &outerIndex, &innerIndex))
{
// Write a number to identify the function if possible, for faster lookup and less bandwidth
bs.Write(true);
bs.WriteCompressed(remoteFunctions[outerIndex]->operator [](innerIndex).functionIndex);
}
else
{
bs.Write(false);
stringCompressor->EncodeString(uniqueIdentifier, 512, &bs, 0);
}
bs.WriteCompressed(bytesOnStack);
bs.WriteAlignedBytes((const unsigned char*) stack, bytesOnStack);
rakPeer->Send(&bs, outgoingPriority, outgoingReliability, outgoingOrderingChannel, systemAddr, false);
// Start writing again after ID_AUTO_RPC_CALL
bs.SetWriteOffset(writeOffset);
}
}
}
else
{
systemAddr = outgoingSystemAddress;
if (systemAddr!=UNASSIGNED_SYSTEM_ADDRESS)
{
if (GetRemoteFunctionIndex(systemAddr, identifier, &outerIndex, &innerIndex))
{
// Write a number to identify the function if possible, for faster lookup and less bandwidth
bs.Write(true);
bs.WriteCompressed(remoteFunctions[outerIndex]->operator [](innerIndex).functionIndex);
}
else
{
bs.Write(false);
stringCompressor->EncodeString(uniqueIdentifier, 512, &bs, 0);
}
bs.WriteCompressed(bytesOnStack);
bs.WriteAlignedBytes((const unsigned char*) stack, bytesOnStack);
rakPeer->Send(&bs, outgoingPriority, outgoingReliability, outgoingOrderingChannel, systemAddr, false);
}
else
return false;
}
return true;
}
void AutoRPC::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
outgoingSystemAddress=UNASSIGNED_SYSTEM_ADDRESS;
outgoingNetworkID=UNASSIGNED_NETWORK_ID;
incomingSystemAddress=UNASSIGNED_SYSTEM_ADDRESS;
}
PluginReceiveResult AutoRPC::OnReceive(RakPeerInterface *peer, Packet *packet)
{
RakNetTime timestamp=0;
unsigned char packetIdentifier, packetDataOffset;
if ( ( unsigned char ) packet->data[ 0 ] == ID_TIMESTAMP )
{
if ( packet->length > sizeof( unsigned char ) + sizeof( RakNetTime ) )
{
packetIdentifier = ( unsigned char ) packet->data[ sizeof( unsigned char ) + sizeof( RakNetTime ) ];
// Required for proper endian swapping
RakNet::BitStream tsBs(packet->data+sizeof(MessageID),packet->length-1,false);
tsBs.Read(timestamp);
packetDataOffset=sizeof( unsigned char )*2 + sizeof( RakNetTime );
}
else
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
else
{
packetIdentifier = ( unsigned char ) packet->data[ 0 ];
packetDataOffset=sizeof( unsigned char );
}
switch (packetIdentifier)
{
case ID_DISCONNECTION_NOTIFICATION:
case ID_CONNECTION_LOST:
OnCloseConnection(peer, packet->systemAddress);
return RR_CONTINUE_PROCESSING;
case ID_AUTO_RPC_CALL:
incomingTimeStamp=timestamp;
incomingSystemAddress=packet->systemAddress;
OnAutoRPCCall(packet->systemAddress, packet->data+packetDataOffset, packet->length-packetDataOffset);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_AUTO_RPC_REMOTE_INDEX:
OnRPCRemoteIndex(packet->systemAddress, packet->data+packetDataOffset, packet->length-packetDataOffset);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_AUTO_RPC_UNKNOWN_REMOTE_INDEX:
OnRPCUnknownRemoteIndex(packet->systemAddress, packet->data+packetDataOffset, packet->length-packetDataOffset, timestamp);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
return RR_CONTINUE_PROCESSING;
}
void AutoRPC::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
{
(void) peer;
if (remoteFunctions.Has(systemAddress))
{
DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *theList = remoteFunctions.Get(systemAddress);
unsigned i;
for (i=0; i < theList->Size(); i++)
{
if (theList->operator [](i).identifier.uniqueIdentifier)
rakFree(theList->operator [](i).identifier.uniqueIdentifier);
}
delete theList;
remoteFunctions.Delete(systemAddress);
}
}
void AutoRPC::OnAutoRPCCall(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes)
{
RakNet::BitStream bs(data,lengthInBytes,false);
bool hasParameterCount=false;
char parameterCount;
char inputStack[ARPC_MAX_STACK_SIZE];
NetworkIDObject *networkIdObject;
NetworkID networkId;
bool hasNetworkId=false;
bool hasFunctionIndex=false;
unsigned int functionIndex;
unsigned int bytesOnStack;
char strIdentifier[512];
int numberOfBitsUsed;
incomingExtraData.Reset();
bs.Read(hasParameterCount);
if (hasParameterCount)
bs.Read(parameterCount);
else
parameterCount=-1;
bs.ReadCompressed(numberOfBitsUsed);
if (numberOfBitsUsed > (int) incomingExtraData.GetNumberOfBitsAllocated())
incomingExtraData.AddBitsAndReallocate(numberOfBitsUsed-(int) incomingExtraData.GetNumberOfBitsAllocated());
bs.ReadBits(incomingExtraData.GetData(), numberOfBitsUsed, false);
incomingExtraData.SetWriteOffset(numberOfBitsUsed);
// const unsigned int outputStackSize = ARPC_MAX_STACK_SIZE+128*4; // Enough padding to round up to 4 for each parameter, max 128 parameters
// char outputStack[outputStackSize];
bs.Read(hasNetworkId);
if (hasNetworkId)
{
bs.Read(networkId);
if (networkIdManager==0 && (networkIdManager=rakPeer->GetNetworkIDManager())==0)
{
// Failed - Tried to call object member, however, networkIDManager system was never registered
SendError(systemAddress, RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE, "");
return;
}
networkIdObject = (NetworkIDObject*) networkIdManager->GET_OBJECT_FROM_ID(networkId);
if (networkIdObject==0)
{
// Failed - Tried to call object member, object does not exist (deleted?)
SendError(systemAddress, RPC_ERROR_OBJECT_DOES_NOT_EXIST, "");
return;
}
}
else
{
networkIdObject=0;
}
bs.AlignReadToByteBoundary();
bs.Read(hasFunctionIndex);
if (hasFunctionIndex)
bs.ReadCompressed(functionIndex);
else
stringCompressor->DecodeString(strIdentifier,512,&bs,0);
bs.ReadCompressed(bytesOnStack);
bs.ReadAlignedBytes((unsigned char *) inputStack,bytesOnStack);
if (hasFunctionIndex)
{
if (functionIndex>localFunctions.Size())
{
// Failed - other system specified a totally invalid index
// Possible causes: Bugs, attempts to crash the system, requested function not registered
SendError(systemAddress, RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE, "");
return;
}
// it was actually a mistake to implement ID_AUTO_RPC_UNKNOWN_REMOTE_INDEX. This hides the more relevant return code RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED and more importantly can result in the calls being out of order since it takes extra communication steps.
/*
if (localFunctions[functionIndex].functionPtr==0)
{
// Failed - Function index lookup failure. Try passing back what was sent to us, and requesting the string
RakNet::BitStream out;
if (incomingTimeStamp!=0)
{
out.Write((MessageID)ID_TIMESTAMP);
out.Write(incomingTimeStamp);
}
out.Write((MessageID)ID_AUTO_RPC_UNKNOWN_REMOTE_INDEX);
if (parameterCount>=0)
{
out.Write(true);
out.Write(parameterCount);
}
else
{
out.Write(false);
}
out.WriteCompressed(functionIndex);
out.WriteCompressed(numberOfBitsUsed);
out.Write(&incomingExtraData);
out.Write(hasNetworkId);
if (hasNetworkId)
out.Write(networkId);
out.WriteCompressed(bytesOnStack);
out.WriteAlignedBytes((const unsigned char*) inputStack, bytesOnStack);
rakPeer->Send(&out, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false);
return;
}
*/
}
else
{
// Find the registered function with this str
for (functionIndex=0; functionIndex < localFunctions.Size(); functionIndex++)
{
if (localFunctions[functionIndex].identifier.isObjectMember == (networkIdObject!=0) &&
strcmp(localFunctions[functionIndex].identifier.uniqueIdentifier, strIdentifier)==0)
{
// SEND RPC MAPPING
RakNet::BitStream outgoingBitstream;
outgoingBitstream.Write((MessageID)ID_AUTO_RPC_REMOTE_INDEX);
outgoingBitstream.Write(hasNetworkId);
outgoingBitstream.WriteCompressed(functionIndex);
stringCompressor->EncodeString(strIdentifier,512,&outgoingBitstream,0);
rakPeer->Send(&outgoingBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemAddress, false);
break;
}
}
if (functionIndex==localFunctions.Size())
{
for (functionIndex=0; functionIndex < localFunctions.Size(); functionIndex++)
{
if (strcmp(localFunctions[functionIndex].identifier.uniqueIdentifier, strIdentifier)==0)
{
if (localFunctions[functionIndex].identifier.isObjectMember==true && networkIdObject==0)
{
// Failed - Calling C++ function as C function
SendError(systemAddress, RPC_ERROR_CALLING_CPP_AS_C, strIdentifier);
return;
}
if (localFunctions[functionIndex].identifier.isObjectMember==false && networkIdObject!=0)
{
// Failed - Calling C function as C++ function
SendError(systemAddress, RPC_ERROR_CALLING_C_AS_CPP, strIdentifier);
return;
}
}
}
SendError(systemAddress, RPC_ERROR_FUNCTION_NOT_REGISTERED, strIdentifier);
return;
}
}
if (localFunctions[functionIndex].functionPtr==0)
{
// Failed - Function was previously registered, but isn't registered any longer
SendError(systemAddress, RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED, localFunctions[functionIndex].identifier.uniqueIdentifier);
return;
}
if (bytesOnStack > ARPC_MAX_STACK_SIZE)
{
// Failed - Not enough bytes on predetermined stack. Shouldn't hit this since the sender also uses this value
SendError(systemAddress, RPC_ERROR_STACK_TOO_SMALL, localFunctions[functionIndex].identifier.uniqueIdentifier);
return;
}
if (localFunctions[functionIndex].parameterCount>=0 && parameterCount>=0 && parameterCount!=localFunctions[functionIndex].parameterCount)
{
// Failed - The number of parameters that this function has was explicitly specified, and does not match up.
SendError(systemAddress, RPC_ERROR_INCORRECT_NUMBER_OF_PARAMETERS, localFunctions[functionIndex].identifier.uniqueIdentifier);
return;
}
// unsigned int bytesWritten;
// unsigned char numParameters;
// unsigned int parameterLengths[64]; // 64 is arbitrary, just needs to be more than whatever might be serialized
GenRPC::CallParams call;
// this is the dynamic cast error handling
void* deserialized_this = localFunctions[functionIndex].functionPtr.computeThis( networkIdObject );
#ifdef AUTO_RPC_USE_DYNAMIC_CAST
if ( networkIdObject && !deserialized_this )
{
// This needs its only error message - this happens when dynamic_cast<YourClass*>( networdIdObject )
// fails - i.e. you don't inherit from NetworkIDObject.
SendError(systemAddress, RPC_ERROR_STACK_DESERIALIZATION_FAILED, strIdentifier);
return;
}
#endif
if ( !DeserializeParametersAndBuildCall(call, inputStack, bytesOnStack, this, deserialized_this ) )
{
// Failed - Couldn't deserialize
SendError(systemAddress, RPC_ERROR_STACK_DESERIALIZATION_FAILED, strIdentifier);
return;
}
strncpy(currentExecution, localFunctions[functionIndex].identifier.uniqueIdentifier, sizeof(currentExecution)-1);
if (!CallWithStack( call, localFunctions[functionIndex].functionPtr.computeFuncAddr( networkIdObject ))) {
// Failed - Couldn't deserialize
SendError(systemAddress, RPC_ERROR_STACK_DESERIALIZATION_FAILED, strIdentifier);
return;
}
currentExecution[0]=0;
}
void AutoRPC::OnRPCRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes)
{
// A remote system has given us their internal index for a particular function.
// Store it and use it from now on, to save bandwidth and search time
bool objectExists;
char strIdentifier[512];
unsigned int insertionIndex;
unsigned int remoteIndex;
RemoteRPCFunction newRemoteFunction;
RakNet::BitStream bs(data,lengthInBytes,false);
RPCIdentifier identifier;
bs.Read(identifier.isObjectMember);
bs.ReadCompressed(remoteIndex);
stringCompressor->DecodeString(strIdentifier,512,&bs,0);
identifier.uniqueIdentifier=strIdentifier;
if (strIdentifier[0]==0)
return;
DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *theList;
if (remoteFunctions.Has(systemAddress))
{
theList = remoteFunctions.Get(systemAddress);
insertionIndex=theList->GetIndexFromKey(identifier, &objectExists);
if (objectExists==false)
{
newRemoteFunction.functionIndex=remoteIndex;
newRemoteFunction.identifier.isObjectMember=identifier.isObjectMember;
newRemoteFunction.identifier.uniqueIdentifier = (char*) rakMalloc(strlen(strIdentifier)+1);
strcpy(newRemoteFunction.identifier.uniqueIdentifier, strIdentifier);
theList->InsertAtIndex(newRemoteFunction, insertionIndex);
}
}
else
{
theList = new DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp>;
newRemoteFunction.functionIndex=remoteIndex;
newRemoteFunction.identifier.isObjectMember=identifier.isObjectMember;
newRemoteFunction.identifier.uniqueIdentifier = (char*) rakMalloc(strlen(strIdentifier)+1);
strcpy(newRemoteFunction.identifier.uniqueIdentifier, strIdentifier);
theList->InsertAtEnd(newRemoteFunction);
remoteFunctions.SetNew(systemAddress,theList);
}
}
void AutoRPC::OnRPCUnknownRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes, RakNetTime timestamp)
{
char inputStack[ARPC_MAX_STACK_SIZE];
NetworkID networkId;
bool hasNetworkId=false;
unsigned int functionIndex;
unsigned int bytesOnStack;
int numberOfBitsUsed;
char parameterCount;
bool hasParameterCount=false;
RakNet::BitStream extraData;
RakNet::BitStream bs(data,lengthInBytes,false);
bs.Read(hasParameterCount);
if (hasParameterCount)
bs.Read(parameterCount);
bs.ReadCompressed(functionIndex);
bs.ReadCompressed(numberOfBitsUsed);
extraData.AddBitsAndReallocate(numberOfBitsUsed);
bs.ReadBits(extraData.GetData(), numberOfBitsUsed, false);
extraData.SetWriteOffset(numberOfBitsUsed);
bs.Read(hasNetworkId);
if (hasNetworkId)
bs.Read(networkId);
bs.ReadCompressed(bytesOnStack);
bs.ReadAlignedBytes((unsigned char*) inputStack, bytesOnStack);
unsigned outerIndex;
if (remoteFunctions.Has(systemAddress))
{
outerIndex = remoteFunctions.GetIndexAtKey(systemAddress);
DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *theList = remoteFunctions[outerIndex];
unsigned i;
for (i=0; i < theList->Size(); i++)
{
if (theList->operator [](i).functionIndex==functionIndex)
{
RakNet::BitStream out;
// Recover by resending the RPC with the function identifier string this time
if (timestamp!=0)
{
out.Write((MessageID)ID_TIMESTAMP);
out.Write(timestamp);
}
out.Write((MessageID)ID_AUTO_RPC_CALL);
if (parameterCount>=0)
{
out.Write(true);
out.Write(parameterCount);
}
else
{
out.Write(false);
}
out.WriteCompressed(numberOfBitsUsed);
out.Write(&extraData);
out.Write(hasNetworkId);
if (hasNetworkId)
out.Write(networkId);
out.AlignWriteToByteBoundary();
out.Write(false);
stringCompressor->EncodeString(theList->operator [](i).identifier.uniqueIdentifier, 512, &out, 0);
out.WriteCompressed(bytesOnStack);
out.WriteAlignedBytes((const unsigned char*) inputStack, bytesOnStack);
rakPeer->Send(&out, outgoingPriority, outgoingReliability, outgoingOrderingChannel, systemAddress, false);
return;
}
}
}
// Failed to recover, inform the user
Packet *p = rakPeer->AllocatePacket(sizeof(MessageID)+sizeof(unsigned char));
RakNet::BitStream bs2(p->data, sizeof(MessageID)+sizeof(unsigned char), false);
bs2.SetWriteOffset(0);
bs2.Write((MessageID)ID_RPC_REMOTE_ERROR);
bs2.Write((unsigned char)RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED);
stringCompressor->EncodeString("",256,&bs,0);
p->systemAddress=systemAddress;
rakPeer->PushBackPacket(p, false);
}
void AutoRPC::SendError(SystemAddress target, unsigned char errorCode, const char *functionName)
{
RakNet::BitStream bs;
bs.Write((MessageID)ID_RPC_REMOTE_ERROR);
bs.Write(errorCode);
stringCompressor->EncodeString(functionName,256,&bs,0);
rakPeer->Send(&bs, HIGH_PRIORITY, RELIABLE_ORDERED, 0, target, false);
}
void AutoRPC::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
Clear();
}
void AutoRPC::Clear(void)
{
unsigned i,j;
for (j=0; j < remoteFunctions.Size(); j++)
{
DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *theList = remoteFunctions[j];
for (i=0; i < theList->Size(); i++)
{
if (theList->operator [](i).identifier.uniqueIdentifier)
rakFree(theList->operator [](i).identifier.uniqueIdentifier);
}
delete theList;
}
for (i=0; i < localFunctions.Size(); i++)
{
if (localFunctions[i].identifier.uniqueIdentifier)
rakFree(localFunctions[i].identifier.uniqueIdentifier);
}
localFunctions.Clear();
remoteFunctions.Clear();
outgoingExtraData.Reset();
incomingExtraData.Reset();
}
unsigned AutoRPC::GetLocalFunctionIndex(AutoRPC::RPCIdentifier identifier)
{
unsigned i;
for (i=0; i < localFunctions.Size(); i++)
{
if (localFunctions[i].identifier.isObjectMember==identifier.isObjectMember &&
strcmp(localFunctions[i].identifier.uniqueIdentifier,identifier.uniqueIdentifier)==0)
return i;
}
return (unsigned) -1;
}
bool AutoRPC::GetRemoteFunctionIndex(SystemAddress systemAddress, AutoRPC::RPCIdentifier identifier, unsigned int *outerIndex, unsigned int *innerIndex)
{
bool objectExists=false;
if (remoteFunctions.Has(systemAddress))
{
*outerIndex = remoteFunctions.GetIndexAtKey(systemAddress);
DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *theList = remoteFunctions[*outerIndex];
*innerIndex = theList->GetIndexFromKey(identifier, &objectExists);
}
return objectExists;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

653
thirdparty/raknet/Source/AutoRPC.h vendored Normal file
View File

@@ -0,0 +1,653 @@
/// \file
/// \brief Automatically serializing and deserializing RPC system. More advanced RPC, but possibly not cross-platform
/// \note Semi-depreciated. RPC3 found at DependentExtensions/RPC3 has more features and is easier to use. But if you do not want to link with Boost this version still works.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __AUTO_RPC_H
#define __AUTO_RPC_H
class RakPeerInterface;
class NetworkIDManager;
#include "PluginInterface.h"
#include "DS_Map.h"
#include "PacketPriority.h"
#include "RakNetTypes.h"
#include "BitStream.h"
#include "Gen_RPC8.h"
#include "RakString.h"
#include "NetworkIDObject.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// \defgroup AUTO_RPC_GROUP AutoRPC
/// \ingroup PLUGINS_GROUP
namespace RakNet
{
/// Maximum amount of data that can be passed on the stack in a function call
#define ARPC_MAX_STACK_SIZE 65536
/// Get a pointer to a function member of a C++ class.
/// \note Recommended you use ARPC_REGISTER_CPP_FUNCTION0 to ARPC_REGISTER_CPP_FUNCTION9 (below)
/// \note Cannot validate the number of parameters is correctly passed.
/// \note You must use one of these macros, or the code will be broken.
/// \param[in] autoRPCInstance A pointer to an instance of AutoRPC
/// \param[in] _IDENTIFIER_ C string identifier to use on the remote system to call the function
/// \param[in] _RETURN_ Return value of the function
/// \param[in] _CLASS_ Base-most class of the containing class that contains your function
/// \param[in] _FUNCTION_ Name of the function
/// \param[in] _PARAMS_ Parameter list, include parenthesis
#define ARPC_REGISTER_CPP_FUNCTION(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS_) \
(autoRPCInstance)->RegisterFunction( (_IDENTIFIER_), GenRPC::PMFWrapper<_CLASS_, _RETURN_(AUTO_RPC_CALLSPEC _CLASS_::*) _PARAMS_ >( &_CLASS_::_FUNCTION_), true, -1 )
/// Get a pointer to a function member of a C++ class.
/// \note Recommended you use ARPC_REGISTER_CPP_FUNCTION0 to ARPC_REGISTER_CPP_FUNCTION9 (below)
/// \note Cannot validate the number of parameters is correctly passed.
/// \note You must use one of these macros, or the code will be broken.
/// \param[in] autoRPCInstance A pointer to an instance of AutoRPC
/// \param[in] _IDENTIFIER_ C string identifier to use on the remote system to call the function
/// \param[in] _RETURN_ Return value of the function
/// \param[in] _CLASS_ Base-most class of the containing class that contains your function
/// \param[in] _FUNCTION_ Name of the function
/// \param[in] _PARAMS_ Parameter list, include parenthesis
/// \param[in] _PARAM_COUNT_ Number of parameters.
#define ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS_, _PARAM_COUNT_) \
(autoRPCInstance)->RegisterFunction( (_IDENTIFIER_), GenRPC::PMFWrapper<_CLASS_, _RETURN_(AUTO_RPC_CALLSPEC _CLASS_::*) _PARAMS_>( &_CLASS_::_FUNCTION_), true, _PARAM_COUNT_ )
// These uses the ISO C99 varadic macros, so, in theory, *should* be 100% standards compliant.
#define ARPC_REGISTER_CPP_FUNCTION0(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_) \
(autoRPCInstance)->RegisterFunction((_IDENTIFIER_), GenRPC::PMFWrapper<_CLASS_, _RETURN_(AUTO_RPC_CALLSPEC _CLASS_::*)()>( &_CLASS_::_FUNCTION_ ), true, 0 )
/// Get a pointer to a function member of a C++ class
/// \param[in] autoRPCInstance A pointer to an instance of AutoRPC
/// \param[in] _IDENTIFIER_ C string identifier to use on the remote system to call the function
/// \param[in] _RETURN_ Return value of the function
/// \param[in] _CLASS_ Base-most class of the containing class that contains your function
/// \param[in] _FUNCTION_ Name of the function
// NB I have no idea why the "-1" is here - it may be a bug, but it was present in the original macros.
#define ARPC_REGISTER_CPP_FUNCTION_N(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
(autoRPCInstance)->RegisterFunction( (_IDENTIFIER_), GenRPC::PMFWrapper<_CLASS_, _RETURN_(AUTO_RPC_CALLSPEC _CLASS_::*)(__VA_ARGS__)>( &_CLASS_::_FUNCTION_), true, GenRPC::countFuncArgs( &_CLASS_::_FUNCTION_ ) - 1 )
// These are historic - you can just use the ARPC_REGISTER_CPP_FUNCTION_N
#define ARPC_REGISTER_CPP_FUNCTION1(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION2(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION3(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION4(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION5(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION6(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION7(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION8(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
#define ARPC_REGISTER_CPP_FUNCTION9(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, ...) \
ARPC_REGISTER_CPP_FUNCTION_N( autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, __VA_ARGS__)
/// Error codes returned by a remote system as to why an RPC function call cannot execute
/// Follows packet ID ID_RPC_REMOTE_ERROR
/// Name of the function will be appended, if available. Read as follows:
/// char outputBuff[256];
/// stringCompressor->DecodeString(outputBuff,256,&RakNet::BitStream(p->data+sizeof(MessageID)+1,p->length-sizeof(MessageID)-1,false),0);
/// printf("Function: %s\n", outputBuff);
enum RPCErrorCodes
{
/// AutoRPC::SetNetworkIDManager() was not called, and it must be called to call a C++ object member
RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE,
/// Cannot execute C++ object member call because the object specified by SetRecipientObject() does not exist on this system
RPC_ERROR_OBJECT_DOES_NOT_EXIST,
/// Internal error, index optimization for function lookup does not exist
RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE,
/// Named function was not registered with RegisterFunction(). Check your spelling.
RPC_ERROR_FUNCTION_NOT_REGISTERED,
/// Named function was registered, but later unregistered with UnregisterFunction() and can no longer be called.
RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED,
/// SetRecipientObject() was not called before Call(), but RegisterFunction() was called with isObjectMember=true
/// If you intended to call a CPP function, call SetRecipientObject() with a valid object first.
RPC_ERROR_CALLING_CPP_AS_C,
/// SetRecipientObject() was called before Call(), but RegisterFunction() was called with isObjectMember=false
/// If you intended to call a C function, call SetRecipientObject(UNASSIGNED_NETWORK_ID) first.
RPC_ERROR_CALLING_C_AS_CPP,
/// Internal error, passed stack is bigger than current stack. Check that the version is the same on both systems.
RPC_ERROR_STACK_TOO_SMALL,
/// Internal error, formatting error with how the stack was serialized
RPC_ERROR_STACK_DESERIALIZATION_FAILED,
/// The \a parameterCount parameter passed to RegisterFunction() on this system does not match the \a parameterCount parameter passed to SendCall() on the remote system.
RPC_ERROR_INCORRECT_NUMBER_OF_PARAMETERS,
};
/// The AutoRPC plugin allows you to call remote functions as if they were local functions, using the standard function call syntax
/// No serialization or deserialization is needed.
/// Advantages are that this is easier to use than regular RPC system.
/// Disadvantages is that all parameters must be passable on the stack using memcpy (shallow copy). For other types of parameters, use SetOutgoingExtraData() and GetIncomingExtraData()
/// Pointers are automatically dereferenced and the contents copied with memcpy
/// Use the old system, or regular message passing, if you need greater flexibility
/// \note Semi-depreciated. Use DependentExtensions\RPC3 unless you do not want to link with Boost
/// \ingroup AUTO_RPC_GROUP
class AutoRPC : public PluginInterface
{
public:
/// Constructor
AutoRPC();
/// Destructor
virtual ~AutoRPC();
/// Sets the network ID manager to use for object lookup
/// Required to call C++ object member functions via SetRecipientObject()
/// \param[in] idMan Pointer to the network ID manager to use
void SetNetworkIDManager(NetworkIDManager *idMan);
/// Registers a function pointer to be callable given an identifier for the pointer.
/// \param[in] uniqueIdentifier String identifying the function. Recommended that this is the name of the function
/// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION
/// \param[in] isObjectMember false if a C function. True if a member function of an object (C++)
/// \param[in] parameterCount Optional parameter to tell the system how many parameters this function has. If specified, and the wrong number of parameters are called by the remote system, the call is rejected. -1 indicates undefined
/// \return True on success, false on uniqueIdentifier already used
/// \note Only use this for C functions; for C++ use one of the ARPC_REGISTER_CPP_FUNCTION_XXX macros
bool RegisterFunction(const char *uniqueIdentifier, void *functionPtr, bool isObjectMember, char parameterCount=-1);
/// \internal Registers a function - using a PMF structure.
bool RegisterFunction(const char *uniqueIdentifier, GenRPC::PMF pmf, bool isObjectMember, char parameterCount=-1);
/// Unregisters a function pointer to be callable given an identifier for the pointer
/// \note This is not safe to call while connected
/// \param[in] uniqueIdentifier String identifying the function.
/// \param[in] isObjectMember false if a C function. True if a member function of an object (C++)
/// \return True on success, false on function was not previously or is not currently registered.
bool UnregisterFunction(const char *uniqueIdentifier, bool isObjectMember);
/// Send or stop sending a timestamp with all following calls to Call()
/// Use GetLastSenderTimestamp() to read the timestamp.
/// \param[in] timeStamp Non-zero to pass this timestamp using the ID_TIMESTAMP system. 0 to clear passing a timestamp.
void SetTimestamp(RakNetTime timeStamp);
/// Set parameters to pass to RakPeer::Send() for all following calls to Call()
/// Deafults to HIGH_PRIORITY, RELIABLE_ORDERED, ordering channel 0
/// \param[in] priority See RakPeer::Send()
/// \param[in] reliability See RakPeer::Send()
/// \param[in] orderingChannel See RakPeer::Send()
void SetSendParams(PacketPriority priority, PacketReliability reliability, char orderingChannel);
/// Set system to send to for all following calls to Call()
/// Defaults to UNASSIGNED_SYSTEM_ADDRESS, broadcast=true
/// \param[in] systemAddress See RakPeer::Send()
/// \param[in] broadcast See RakPeer::Send()
void SetRecipientAddress(SystemAddress systemAddress, bool broadcast);
/// Set the NetworkID to pass for all following calls to Call()
/// Defaults to UNASSIGNED_NETWORK_ID (none)
/// If set, the remote function will be considered a C++ function, e.g. an object member function
/// If set to UNASSIGNED_NETWORK_ID (none), the remote function will be considered a C function
/// If this is set incorrectly, you will get back either RPC_ERROR_CALLING_C_AS_CPP or RPC_ERROR_CALLING_CPP_AS_C
/// \sa NetworkIDManager
/// \param[in] networkID Returned from NetworkIDObject::GetNetworkID()
void SetRecipientObject(NetworkID networkID);
/// Write extra data to pass for all following calls to Call()
/// Use BitStream::Reset to clear extra data. Don't forget to do this or you will waste bandwidth.
/// \return A bitstream you can write to to send extra data with each following call to Call()
RakNet::BitStream *SetOutgoingExtraData(void);
/// If the last received function call has a timestamp included, it is stored and can be retrieved with this function.
/// \return 0 if the last call did not have a timestamp, else non-zero
RakNetTime GetLastSenderTimestamp(void) const;
/// Returns the system address of the last system to send us a received function call
/// Equivalent to the old system RPCParameters::sender
/// \return Last system to send an RPC call using this system
SystemAddress GetLastSenderAddress(void) const;
/// Returns the instance of RakPeer this plugin was attached to
RakPeerInterface *GetRakPeer(void) const;
/// Returns the currently running RPC call identifier, set from RegisterFunction::uniqueIdentifier
/// Returns an empty string "" if none
/// \Return which RPC call is currently running
const char *GetCurrentExecution(void) const;
/// Gets the bitstream written to via SetOutgoingExtraData().
/// Data is updated with each incoming function call
/// \return A bitstream you can read from with extra data that was written with SetOutgoingExtraData();
RakNet::BitStream *GetIncomingExtraData(void);
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
bool Call(const char *uniqueIdentifier){
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 0);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1>
bool Call(const char *uniqueIdentifier, P1 p1) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 1);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 2);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 3);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3, class P4>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 4);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3, class P4, class P5>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 5);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3, class P4, class P5, class P6>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 6);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, true, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 7);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 ) {
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, p8, true, true, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 8);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID){
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 0);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 1);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 2);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 3);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3, class P4>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 4);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3, class P4, class P5>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 5);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3, class P4, class P5, class P6>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 6);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, true, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 7);
}
/// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject()
/// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function
/// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData()
/// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this
/// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system
/// \param[in] timeStamp See SetTimestamp()
/// \param[in] priority See SetSendParams()
/// \param[in] reliability See SetSendParams()
/// \param[in] orderingChannel See SetSendParams()
/// \param[in] systemAddress See SetRecipientAddress()
/// \param[in] broadcast See SetRecipientAddress()
/// \param[in] networkID See SetRecipientObject()
template <class P1, class P2, class P3, class P4, class P5, class P6, class P7, class P8>
bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 ) {
SetTimestamp(timeStamp);
SetSendParams(priority, reliability, orderingChannel);
SetRecipientAddress(systemAddress, broadcast);
SetRecipientObject(networkID);
char stack[ARPC_MAX_STACK_SIZE];
unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, p8, true, true, true, true, true, true, true, true);
return SendCall(uniqueIdentifier, stack, bytesOnStack, 8);
}
// If you need more than 8 parameters, just add it here...
// ---------------------------- ALL INTERNAL AFTER HERE ----------------------------
/// \internal
/// Identifies an RPC function, by string identifier and if it is a C or C++ function
struct RPCIdentifier
{
char *uniqueIdentifier;
bool isObjectMember;
};
/// \internal
/// The RPC identifier, and a pointer to the function
struct LocalRPCFunction
{
RPCIdentifier identifier;
GenRPC::PMF functionPtr;
char parameterCount;
};
/// \internal
/// The RPC identifier, and the index of the function on a remote system
struct RemoteRPCFunction
{
RPCIdentifier identifier;
unsigned int functionIndex;
};
/// \internal
static int RemoteRPCFunctionComp( const RPCIdentifier &key, const RemoteRPCFunction &data );
/// \internal
/// Sends the RPC call, with a given serialized stack
bool SendCall(const char *uniqueIdentifier, const char *stack, unsigned int bytesOnStack, char parameterCount);
protected:
// --------------------------------------------------------------------------------------------
// Packet handling functions
// --------------------------------------------------------------------------------------------
void OnAttach(RakPeerInterface *peer);
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
virtual void OnAutoRPCCall(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes);
virtual void OnRPCRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes);
virtual void OnRPCUnknownRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes, RakNetTime timestamp);
virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress);
virtual void OnShutdown(RakPeerInterface *peer);
void Clear(void);
void SendError(SystemAddress target, unsigned char errorCode, const char *functionName);
unsigned GetLocalFunctionIndex(RPCIdentifier identifier);
bool GetRemoteFunctionIndex(SystemAddress systemAddress, RPCIdentifier identifier, unsigned int *outerIndex, unsigned int *innerIndex);
DataStructures::List<LocalRPCFunction> localFunctions;
DataStructures::Map<SystemAddress, DataStructures::OrderedList<RPCIdentifier, RemoteRPCFunction, AutoRPC::RemoteRPCFunctionComp> *> remoteFunctions;
RakNetTime outgoingTimestamp;
PacketPriority outgoingPriority;
PacketReliability outgoingReliability;
char outgoingOrderingChannel;
SystemAddress outgoingSystemAddress;
bool outgoingBroadcast;
NetworkID outgoingNetworkID;
RakNet::BitStream outgoingExtraData;
RakNetTime incomingTimeStamp;
SystemAddress incomingSystemAddress;
RakNet::BitStream incomingExtraData;
RakPeerInterface *rakPeer;
NetworkIDManager *networkIdManager;
char currentExecution[512];
};
} // End namespace
#endif
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,17 @@
#ifndef __AUTOPATCHER_PATCH_CONTEXT_H
#define __AUTOPATCHER_PATCH_CONTEXT_H
enum PatchContext
{
PC_HASH_WITH_PATCH,
PC_WRITE_FILE,
PC_ERROR_FILE_WRITE_FAILURE,
PC_ERROR_PATCH_TARGET_MISSING,
PC_ERROR_PATCH_APPLICATION_FAILURE,
PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE,
PC_NOTICE_WILL_COPY_ON_RESTART,
PC_NOTICE_FILE_DOWNLOADED,
PC_NOTICE_FILE_DOWNLOADED_PATCH,
};
#endif

View File

@@ -0,0 +1,53 @@
/// \file
/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H
#define __AUTOPATCHER_REPOSITORY_INTERFACE_H
class FileList;
namespace RakNet
{
class BitStream;
}
/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions.
class AutopatcherRepositoryInterface
{
public:
/// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called.
/// \param[in] applicationName A null terminated string identifying the application
/// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData
/// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData
/// \param[in] An input date, in whatever format your repository uses
/// \param[out] currentDate The current server date, in whatever format your repository uses
/// \return True on success, false on failure.
virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, const char *sinceDate, char currentDate[64])=0;
/// Get patches (or files) for every file in input, assuming that input has a hash for each of those files.
/// \param[in] applicationName A null terminated string identifying the application
/// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database.
/// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH
/// \param[out] currentDate The current server date, in whatever format your repository uses
/// \return True on success, false on failure.
virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList, char currentDate[64])=0;
/// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false.
virtual const char *GetLastError(void) const=0;
};
#endif

1824
thirdparty/raknet/Source/BigTypes.h vendored Normal file

File diff suppressed because it is too large Load Diff

915
thirdparty/raknet/Source/BitStream.cpp vendored Normal file
View File

@@ -0,0 +1,915 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization
#include "BitStream_NoTemplate.cpp"
#else
#include "BitStream.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _XBOX360
#include "Console1Includes.h"
#elif defined(_WIN32)
#include <winsock2.h> // htonl
#include <memory.h>
#include <cmath>
#include <float.h>
#elif defined(_PS3)
#include "Console2Includes.h"
#else
#include <arpa/inet.h>
#include <memory.h>
#include <cmath>
#include <float.h>
#endif
// MSWin uses _copysign, others use copysign...
#ifndef _WIN32
#define _copysign copysign
#endif
using namespace RakNet;
#ifdef _MSC_VER
#pragma warning( push )
#endif
BitStream::BitStream()
{
numberOfBitsUsed = 0;
//numberOfBitsAllocated = 32 * 8;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8;
readOffset = 0;
//data = ( unsigned char* ) rakMalloc( 32 );
data = ( unsigned char* ) stackData;
#ifdef _DEBUG
// assert( data );
#endif
//memset(data, 0, 32);
copyData = true;
}
BitStream::BitStream( const unsigned int initialBytesToAllocate )
{
numberOfBitsUsed = 0;
readOffset = 0;
if (initialBytesToAllocate <= BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = ( unsigned char* ) stackData;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE * 8;
}
else
{
data = ( unsigned char* ) rakMalloc( (size_t) initialBytesToAllocate );
numberOfBitsAllocated = initialBytesToAllocate << 3;
}
#ifdef _DEBUG
assert( data );
#endif
// memset(data, 0, initialBytesToAllocate);
copyData = true;
}
BitStream::BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData )
{
numberOfBitsUsed = lengthInBytes << 3;
readOffset = 0;
copyData = _copyData;
numberOfBitsAllocated = lengthInBytes << 3;
if ( copyData )
{
if ( lengthInBytes > 0 )
{
if (lengthInBytes < BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = ( unsigned char* ) stackData;
numberOfBitsAllocated = BITSTREAM_STACK_ALLOCATION_SIZE << 3;
}
else
{
data = ( unsigned char* ) rakMalloc( (size_t) lengthInBytes );
}
#ifdef _DEBUG
assert( data );
#endif
memcpy( data, _data, (size_t) lengthInBytes );
}
else
data = 0;
}
else
data = ( unsigned char* ) _data;
}
// Use this if you pass a pointer copy to the constructor (_copyData==false) and want to overallocate to prevent reallocation
void BitStream::SetNumberOfBitsAllocated( const BitSize_t lengthInBits )
{
#ifdef _DEBUG
assert( lengthInBits >= ( BitSize_t ) numberOfBitsAllocated );
#endif
numberOfBitsAllocated = lengthInBits;
}
BitStream::~BitStream()
{
if ( copyData && numberOfBitsAllocated > (BITSTREAM_STACK_ALLOCATION_SIZE << 3))
RakFree( data ); // Use realloc and free so we are more efficient than delete and new for resizing
}
void BitStream::Reset( void )
{
// Note: Do NOT reallocate memory because BitStream is used
// in places to serialize/deserialize a buffer. Reallocation
// is a dangerous operation (may result in leaks).
if ( numberOfBitsUsed > 0 )
{
// memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed));
}
// Don't free memory here for speed efficiency
//free(data); // Use realloc and free so we are more efficient than delete and new for resizing
numberOfBitsUsed = 0;
//numberOfBitsAllocated=8;
readOffset = 0;
//data=(unsigned char*)rakMalloc(1);
// if (numberOfBitsAllocated>0)
// memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated));
}
// Write an array or casted stream
void BitStream::Write( const char* input, const unsigned int numberOfBytes )
{
if (numberOfBytes==0)
return;
// Optimization:
if ((numberOfBitsUsed & 7) == 0)
{
AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) );
memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), input, (size_t) numberOfBytes);
numberOfBitsUsed+=BYTES_TO_BITS(numberOfBytes);
}
else
{
WriteBits( ( unsigned char* ) input, numberOfBytes * 8, true );
}
}
void BitStream::Write( BitStream *bitStream)
{
Write(bitStream, bitStream->GetNumberOfBitsUsed());
}
void BitStream::Write( BitStream *bitStream, BitSize_t numberOfBits )
{
AddBitsAndReallocate( numberOfBits );
BitSize_t numberOfBitsMod8;
while (numberOfBits-->0 && bitStream->readOffset + 1 <= bitStream->numberOfBitsUsed)
{
numberOfBitsMod8 = numberOfBitsUsed & 7;
if ( numberOfBitsMod8 == 0 )
{
// New byte
if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) )
{
// Write 1
data[ numberOfBitsUsed >> 3 ] = 0x80;
}
else
{
// Write 0
data[ numberOfBitsUsed >> 3 ] = 0;
}
}
else
{
// Existing byte
if (bitStream->data[ bitStream->readOffset >> 3 ] & ( 0x80 >> ( bitStream->readOffset & 7 ) ) )
data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
// else 0, do nothing
}
bitStream->readOffset++;
numberOfBitsUsed++;
}
}
void BitStream::Write( BitStream &bitStream, BitSize_t numberOfBits )
{
Write(&bitStream, numberOfBits);
}
void BitStream::Write( BitStream &bitStream )
{
Write(&bitStream);
}
bool BitStream::Read( BitStream *bitStream, BitSize_t numberOfBits )
{
if (GetNumberOfUnreadBits() < numberOfBits)
return false;
bitStream->Write(this, numberOfBits);
return true;
}
bool BitStream::Read( BitStream *bitStream )
{
bitStream->Write(this);
return true;
}
bool BitStream::Read( BitStream &bitStream, BitSize_t numberOfBits )
{
if (GetNumberOfUnreadBits() < numberOfBits)
return false;
bitStream.Write(this, numberOfBits);
return true;
}
bool BitStream::Read( BitStream &bitStream )
{
bitStream.Write(this);
return true;
}
// Read an array or casted stream
bool BitStream::Read( char* output, const unsigned int numberOfBytes )
{
// Optimization:
if ((readOffset & 7) == 0)
{
if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed )
return false;
// Write the data
memcpy( output, data + ( readOffset >> 3 ), (size_t) numberOfBytes );
readOffset += numberOfBytes << 3;
return true;
}
else
{
return ReadBits( ( unsigned char* ) output, numberOfBytes * 8 );
}
}
// Sets the read pointer back to the beginning of your data.
void BitStream::ResetReadPointer( void )
{
readOffset = 0;
}
// Sets the write pointer back to the beginning of your data.
void BitStream::ResetWritePointer( void )
{
numberOfBitsUsed = 0;
}
// Write a 0
void BitStream::Write0( void )
{
AddBitsAndReallocate( 1 );
// New bytes need to be zeroed
if ( ( numberOfBitsUsed & 7 ) == 0 )
data[ numberOfBitsUsed >> 3 ] = 0;
numberOfBitsUsed++;
}
// Write a 1
void BitStream::Write1( void )
{
AddBitsAndReallocate( 1 );
BitSize_t numberOfBitsMod8 = numberOfBitsUsed & 7;
if ( numberOfBitsMod8 == 0 )
data[ numberOfBitsUsed >> 3 ] = 0x80;
else
data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
numberOfBitsUsed++;
}
// Returns true if the next data read is a 1, false if it is a 0
bool BitStream::ReadBit( void )
{
bool result = ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) !=0;
readOffset++;
return result;
}
// Align the bitstream to the byte boundary and then write the specified number of bits.
// This is faster than WriteBits but wastes the bits to do the alignment and requires you to call
// SetReadToByteAlignment at the corresponding read position
void BitStream::WriteAlignedBytes( const unsigned char* input, const unsigned int numberOfBytesToWrite )
{
#ifdef _DEBUG
if (numberOfBytesToWrite<=0)
{
assert( numberOfBytesToWrite > 0 );
}
#endif
AlignWriteToByteBoundary();
Write((const char*) input, numberOfBytesToWrite);
}
/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite
void BitStream::WriteAlignedBytesSafe( const char *input, const unsigned int inputLength, const unsigned int maxBytesToWrite )
{
if (input==0 || inputLength==0)
{
WriteCompressed((unsigned int)0);
return;
}
WriteCompressed(inputLength);
WriteAlignedBytes((const unsigned char*) input, inputLength < maxBytesToWrite ? inputLength : maxBytesToWrite);
}
// Read bits, starting at the next aligned bits. Note that the modulus 8 starting offset of the
// sequence must be the same as was used with WriteBits. This will be a problem with packet coalescence
// unless you byte align the coalesced packets.
bool BitStream::ReadAlignedBytes( unsigned char* output, const unsigned int numberOfBytesToRead )
{
#ifdef _DEBUG
assert( numberOfBytesToRead > 0 );
#endif
if ( numberOfBytesToRead <= 0 )
return false;
// Byte align
AlignReadToByteBoundary();
if ( readOffset + ( numberOfBytesToRead << 3 ) > numberOfBitsUsed )
return false;
// Write the data
memcpy( output, data + ( readOffset >> 3 ), (size_t) numberOfBytesToRead );
readOffset += numberOfBytesToRead << 3;
return true;
}
bool BitStream::ReadAlignedBytesSafe( char *input, int &inputLength, const int maxBytesToRead )
{
return ReadAlignedBytesSafe(input,(unsigned int&) inputLength,(unsigned int)maxBytesToRead);
}
bool BitStream::ReadAlignedBytesSafe( char *input, unsigned int &inputLength, const unsigned int maxBytesToRead )
{
if (ReadCompressed(inputLength)==false)
return false;
if (inputLength > maxBytesToRead)
inputLength=maxBytesToRead;
if (inputLength==0)
return true;
return ReadAlignedBytes((unsigned char*) input, inputLength);
}
bool BitStream::ReadAlignedBytesSafeAlloc( char **input, int &inputLength, const unsigned int maxBytesToRead )
{
return ReadAlignedBytesSafeAlloc(input,(unsigned int&) inputLength, maxBytesToRead);
}
bool BitStream::ReadAlignedBytesSafeAlloc( char **input, unsigned int &inputLength, const unsigned int maxBytesToRead )
{
rakFree(*input);
*input=0;
if (ReadCompressed(inputLength)==false)
return false;
if (inputLength > maxBytesToRead)
inputLength=maxBytesToRead;
if (inputLength==0)
return true;
*input = (char*) rakMalloc( (size_t) BITS_TO_BYTES( inputLength ) );
return ReadAlignedBytes((unsigned char*) *input, inputLength);
}
// Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons
void BitStream::AlignWriteToByteBoundary( void )
{
if ( numberOfBitsUsed )
numberOfBitsUsed += 8 - ( (( numberOfBitsUsed - 1 ) & 7) + 1 );
}
// Align the next write and/or read to a byte boundary. This can be used to 'waste' bits to byte align for efficiency reasons
void BitStream::AlignReadToByteBoundary( void )
{
if ( readOffset )
readOffset += 8 - ( (( readOffset - 1 ) & 7 ) + 1 );
}
// Write numberToWrite bits from the input source
void BitStream::WriteBits( const unsigned char* input, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits )
{
if (numberOfBitsToWrite<=0)
return;
AddBitsAndReallocate( numberOfBitsToWrite );
BitSize_t offset = 0;
unsigned char dataByte;
BitSize_t numberOfBitsUsedMod8;
numberOfBitsUsedMod8 = numberOfBitsUsed & 7;
// Faster to put the while at the top surprisingly enough
while ( numberOfBitsToWrite > 0 )
//do
{
dataByte = *( input + offset );
if ( numberOfBitsToWrite < 8 && rightAlignedBits ) // rightAlignedBits means in the case of a partial byte, the bits are aligned from the right (bit 0) rather than the left (as in the normal internal representation)
dataByte <<= 8 - numberOfBitsToWrite; // shift left to get the bits on the left, as in our internal representation
// Writing to a new byte each time
if ( numberOfBitsUsedMod8 == 0 )
* ( data + ( numberOfBitsUsed >> 3 ) ) = dataByte;
else
{
// Copy over the new data.
*( data + ( numberOfBitsUsed >> 3 ) ) |= dataByte >> ( numberOfBitsUsedMod8 ); // First half
if ( 8 - ( numberOfBitsUsedMod8 ) < 8 && 8 - ( numberOfBitsUsedMod8 ) < numberOfBitsToWrite ) // If we didn't write it all out in the first half (8 - (numberOfBitsUsed%8) is the number we wrote in the first half)
{
*( data + ( numberOfBitsUsed >> 3 ) + 1 ) = (unsigned char) ( dataByte << ( 8 - ( numberOfBitsUsedMod8 ) ) ); // Second half (overlaps byte boundary)
}
}
if ( numberOfBitsToWrite >= 8 )
numberOfBitsUsed += 8;
else
numberOfBitsUsed += numberOfBitsToWrite;
if (numberOfBitsToWrite>=8)
numberOfBitsToWrite -= 8;
else
numberOfBitsToWrite=0;
offset++;
}
// } while(numberOfBitsToWrite>0);
}
// Set the stream to some initial data. For internal use
void BitStream::SetData( unsigned char *input )
{
data=input;
copyData=false;
}
// Assume the input source points to a native type, compress and write it
void BitStream::WriteCompressed( const unsigned char* input,
const unsigned int size, const bool unsignedData )
{
BitSize_t currentByte = ( size >> 3 ) - 1; // PCs
unsigned char byteMatch;
if ( unsignedData )
{
byteMatch = 0;
}
else
{
byteMatch = 0xFF;
}
// Write upper bytes with a single 1
// From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
while ( currentByte > 0 )
{
if ( input[ currentByte ] == byteMatch ) // If high byte is byteMatch (0 of 0xff) then it would have the same value shifted
{
bool b = true;
Write( b );
}
else
{
// Write the remainder of the data after writing 0
bool b = false;
Write( b );
WriteBits( input, ( currentByte + 1 ) << 3, true );
// currentByte--;
return ;
}
currentByte--;
}
// If the upper half of the last byte is a 0 (positive) or 16 (negative) then write a 1 and the remaining 4 bits. Otherwise write a 0 and the 8 bites.
if ( ( unsignedData && ( ( *( input + currentByte ) ) & 0xF0 ) == 0x00 ) ||
( unsignedData == false && ( ( *( input + currentByte ) ) & 0xF0 ) == 0xF0 ) )
{
bool b = true;
Write( b );
WriteBits( input + currentByte, 4, true );
}
else
{
bool b = false;
Write( b );
WriteBits( input + currentByte, 8, true );
}
}
// Read numberOfBitsToRead bits to the output source
// alignBitsToRight should be set to true to convert internal bitstream data to userdata
// It should be false if you used WriteBits with rightAlignedBits false
bool BitStream::ReadBits( unsigned char *output, BitSize_t numberOfBitsToRead, const bool alignBitsToRight )
{
#ifdef _DEBUG
// assert( numberOfBitsToRead > 0 );
#endif
if (numberOfBitsToRead<=0)
return false;
if ( readOffset + numberOfBitsToRead > numberOfBitsUsed )
return false;
BitSize_t readOffsetMod8;
BitSize_t offset = 0;
memset( output, 0, (size_t) BITS_TO_BYTES( numberOfBitsToRead ) );
readOffsetMod8 = readOffset & 7;
while ( numberOfBitsToRead > 0 )
{
*( output + offset ) |= *( data + ( readOffset >> 3 ) ) << ( readOffsetMod8 ); // First half
if ( readOffsetMod8 > 0 && numberOfBitsToRead > 8 - ( readOffsetMod8 ) ) // If we have a second half, we didn't read enough bytes in the first half
*( output + offset ) |= *( data + ( readOffset >> 3 ) + 1 ) >> ( 8 - ( readOffsetMod8 ) ); // Second half (overlaps byte boundary)
if (numberOfBitsToRead>=8)
{
numberOfBitsToRead -= 8;
readOffset += 8;
offset++;
}
else
{
int neg = (int) numberOfBitsToRead - 8;
if ( neg < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right
{
if ( alignBitsToRight )
* ( output + offset ) >>= -neg;
readOffset += 8 + neg;
}
else
readOffset += 8;
offset++;
numberOfBitsToRead=0;
}
}
return true;
}
// Assume the input source points to a compressed native type. Decompress and read it
bool BitStream::ReadCompressed( unsigned char* output,
const unsigned int size, const bool unsignedData )
{
unsigned int currentByte = ( size >> 3 ) - 1;
unsigned char byteMatch, halfByteMatch;
if ( unsignedData )
{
byteMatch = 0;
halfByteMatch = 0;
}
else
{
byteMatch = 0xFF;
halfByteMatch = 0xF0;
}
// Upper bytes are specified with a single 1 if they match byteMatch
// From high byte to low byte, if high byte is a byteMatch then write a 1 bit. Otherwise write a 0 bit and then write the remaining bytes
while ( currentByte > 0 )
{
// If we read a 1 then the data is byteMatch.
bool b;
if ( Read( b ) == false )
return false;
if ( b ) // Check that bit
{
output[ currentByte ] = byteMatch;
currentByte--;
}
else
{
// Read the rest of the bytes
if ( ReadBits( output, ( currentByte + 1 ) << 3 ) == false )
return false;
return true;
}
}
// All but the first bytes are byteMatch. If the upper half of the last byte is a 0 (positive) or 16 (negative) then what we read will be a 1 and the remaining 4 bits.
// Otherwise we read a 0 and the 8 bytes
//assert(readOffset+1 <=numberOfBitsUsed); // If this assert is hit the stream wasn't long enough to read from
if ( readOffset + 1 > numberOfBitsUsed )
return false;
bool b;
if ( Read( b ) == false )
return false;
if ( b ) // Check that bit
{
if ( ReadBits( output + currentByte, 4 ) == false )
return false;
output[ currentByte ] |= halfByteMatch; // We have to set the high 4 bits since these are set to 0 by ReadBits
}
else
{
if ( ReadBits( output + currentByte, 8 ) == false )
return false;
}
return true;
}
// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite
void BitStream::AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite )
{
if (numberOfBitsToWrite <= 0)
return;
BitSize_t newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed;
if ( numberOfBitsToWrite + numberOfBitsUsed > 0 && ( ( numberOfBitsAllocated - 1 ) >> 3 ) < ( ( newNumberOfBitsAllocated - 1 ) >> 3 ) ) // If we need to allocate 1 or more new bytes
{
#ifdef _DEBUG
// If this assert hits then we need to specify true for the third parameter in the constructor
// It needs to reallocate to hold all the data and can't do it unless we allocated to begin with
// Often hits if you call Write or Serialize on a read-only bitstream
assert( copyData == true );
#endif
// Less memory efficient but saves on news and deletes
/// Cap to 1 meg buffer to save on huge allocations
newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2;
if (newNumberOfBitsAllocated - ( numberOfBitsToWrite + numberOfBitsUsed ) > 1048576 )
newNumberOfBitsAllocated = numberOfBitsToWrite + numberOfBitsUsed + 1048576;
// BitSize_t newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated );
// Use realloc and free so we are more efficient than delete and new for resizing
BitSize_t amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated );
if (data==(unsigned char*)stackData)
{
if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = ( unsigned char* ) rakMalloc( (size_t) amountToAllocate );
// need to copy the stack data over to our new memory area too
memcpy ((void *)data, (void *)stackData, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ));
}
}
else
{
data = ( unsigned char* ) RakRealloc( data, (size_t) amountToAllocate );
}
#ifdef _DEBUG
assert( data ); // Make sure realloc succeeded
#endif
// memset(data+newByteOffset, 0, ((newNumberOfBitsAllocated-1)>>3) - ((numberOfBitsAllocated-1)>>3)); // Set the new data block to 0
}
if ( newNumberOfBitsAllocated > numberOfBitsAllocated )
numberOfBitsAllocated = newNumberOfBitsAllocated;
}
BitSize_t BitStream::GetNumberOfBitsAllocated(void) const
{
return numberOfBitsAllocated;
}
// Should hit if reads didn't match writes
void BitStream::AssertStreamEmpty( void )
{
assert( readOffset == numberOfBitsUsed );
}
void BitStream::PrintBits( void ) const
{
if ( numberOfBitsUsed <= 0 )
{
printf( "No bits\n" );
return ;
}
for ( BitSize_t counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ); counter++ )
{
BitSize_t stop;
if ( counter == ( numberOfBitsUsed - 1 ) >> 3 )
stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 );
else
stop = 0;
for ( BitSize_t counter2 = 7; counter2 >= stop; counter2-- )
{
if ( ( data[ counter ] >> counter2 ) & 1 )
putchar( '1' );
else
putchar( '0' );
if (counter2==0)
break;
}
putchar( ' ' );
}
putchar( '\n' );
}
// Exposes the data for you to look at, like PrintBits does.
// Data will point to the stream. Returns the length in bits of the stream.
BitSize_t BitStream::CopyData( unsigned char** _data ) const
{
#ifdef _DEBUG
assert( numberOfBitsUsed > 0 );
#endif
*_data = (unsigned char*) rakMalloc( (size_t) BITS_TO_BYTES( numberOfBitsUsed ) );
memcpy( *_data, data, sizeof(unsigned char) * (size_t) ( BITS_TO_BYTES( numberOfBitsUsed ) ) );
return numberOfBitsUsed;
}
// Ignore data we don't intend to read
void BitStream::IgnoreBits( const BitSize_t numberOfBits )
{
readOffset += numberOfBits;
}
void BitStream::IgnoreBytes( const unsigned int numberOfBytes )
{
IgnoreBits(BYTES_TO_BITS(numberOfBytes));
}
// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing!
// Doesn't work with non-aligned data!
void BitStream::SetWriteOffset( const BitSize_t offset )
{
numberOfBitsUsed = offset;
}
/*
BitSize_t BitStream::GetWriteOffset( void ) const
{
return numberOfBitsUsed;
}
// Returns the length in bits of the stream
BitSize_t BitStream::GetNumberOfBitsUsed( void ) const
{
return GetWriteOffset();
}
// Returns the length in bytes of the stream
BitSize_t BitStream::GetNumberOfBytesUsed( void ) const
{
return BITS_TO_BYTES( numberOfBitsUsed );
}
// Returns the number of bits into the stream that we have read
BitSize_t BitStream::GetReadOffset( void ) const
{
return readOffset;
}
// Sets the read bit index
void BitStream::SetReadOffset( const BitSize_t newReadOffset )
{
readOffset=newReadOffset;
}
// Returns the number of bits left in the stream that haven't been read
BitSize_t BitStream::GetNumberOfUnreadBits( void ) const
{
return numberOfBitsUsed - readOffset;
}
// Exposes the internal data
unsigned char* BitStream::GetData( void ) const
{
return data;
}
*/
// If we used the constructor version with copy data off, this makes sure it is set to on and the data pointed to is copied.
void BitStream::AssertCopyData( void )
{
if ( copyData == false )
{
copyData = true;
if ( numberOfBitsAllocated > 0 )
{
unsigned char * newdata = ( unsigned char* ) rakMalloc( (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) );
#ifdef _DEBUG
assert( data );
#endif
memcpy( newdata, data, (size_t) BITS_TO_BYTES( numberOfBitsAllocated ) );
data = newdata;
}
else
data = 0;
}
}
void BitStream::ReverseBytes(unsigned char *input, unsigned char *output, const unsigned int length)
{
for (BitSize_t i=0; i < length; i++)
output[i]=input[length-i-1];
}
void BitStream::ReverseBytesInPlace(unsigned char *data,const unsigned int length)
{
unsigned char temp;
BitSize_t i;
for (i=0; i < (length>>1); i++)
{
temp = data[i];
data[i]=data[length-i-1];
data[length-i-1]=temp;
}
}
bool BitStream::DoEndianSwap(void)
{
#ifndef __BITSTREAM_NATIVE_END
return IsNetworkOrder()==false;
#else
return false;
#endif
}
bool BitStream::IsBigEndian(void)
{
return IsNetworkOrder();
}
bool BitStream::IsNetworkOrder(void)
{
#if defined(_PS3)
return true;
#else
static bool isNetworkOrder=(htonl(12345) == 12345);
return isNetworkOrder;
#endif
}
bool BitStream::Read(char *var)
{
return RakString::Deserialize(var,this);
}
bool BitStream::Read(unsigned char *var)
{
return RakString::Deserialize((char*) var,this);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif // #if _MSC_VER < 1299

1696
thirdparty/raknet/Source/BitStream.h vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,784 @@
/// \file
/// \brief This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization
#ifndef __BITSTREAM_H
#define __BITSTREAM_H
#include "RakMemoryOverride.h"
#include "RakNetDefines.h"
#include "Export.h"
#include "RakNetTypes.h"
#include <assert.h>
#if defined(_PS3)
#include <math.h>
#else
#include <cmath>
#endif
#include <float.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// Arbitrary size, just picking something likely to be larger than most packets
#define BITSTREAM_STACK_ALLOCATION_SIZE 256
/// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common.
/// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes.
namespace RakNet
{
/// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well.
/// \sa BitStreamSample.txt
class RAK_DLL_EXPORT BitStream : public RakNet::RakMemoryOverride
{
public:
/// Default Constructor
BitStream();
/// Create the bitstream, with some number of bytes to immediately allocate.
/// There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE.
/// In that case all it does is save you one or more realloc calls.
/// \param[in] initialBytesToAllocate the number of bytes to pre-allocate.
BitStream( int initialBytesToAllocate );
/// Initialize the BitStream, immediately setting the data it contains to a predefined pointer.
/// Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data.
/// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory
/// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows:
/// \code
/// RakNet::BitStream bs(packet->data, packet->length, false);
/// \endcode
/// \param[in] _data An array of bytes.
/// \param[in] lengthInBytes Size of the \a _data.
/// \param[in] _copyData true or false to make a copy of \a _data or not.
BitStream( unsigned char* _data, unsigned int lengthInBytes, bool _copyData );
/// Destructor
~BitStream();
/// Resets the bitstream for reuse.
void Reset( void );
/// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] var The value to write
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool Serialize(bool writeToBitstream, bool &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, unsigned char &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, char &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, unsigned short &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, short &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, unsigned int &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, int &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, unsigned long &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, long &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, long long &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, float &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, double &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
bool Serialize(bool writeToBitstream, long double &var){if (writeToBitstream)Write(var);else return Read(var); return true;}
/// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value
/// the current value will be written. Otherwise, a single bit will be written
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] currentValue The current value to write
/// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true.
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeDelta(bool writeToBitstream, bool &currentValue, bool lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned char &currentValue, unsigned char lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, char &currentValue, char lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned short &currentValue, unsigned short lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, short &currentValue, short lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned int &currentValue, unsigned int lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, int &currentValue, int lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned long &currentValue, unsigned long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, long long &currentValue, long long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned long long &currentValue, unsigned long long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, float &currentValue, float lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, double &currentValue, double lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, long double &currentValue, long double lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;}
/// Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] currentValue The current value to write
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeDelta(bool writeToBitstream, bool &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned char &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, char &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned short &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, short &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned int &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, int &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned long &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, long long &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, unsigned long long &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, float &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, double &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
bool SerializeDelta(bool writeToBitstream, long double &currentValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;}
/// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] var The value to write
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeCompressed(bool writeToBitstream, bool &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, unsigned char &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, char &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, unsigned short &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, short &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, unsigned int &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, int &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, unsigned long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, long long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, float &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, double &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
bool SerializeCompressed(bool writeToBitstream, long double &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;}
/// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value
/// the current value will be written. Otherwise, a single bit will be written
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] currentValue The current value to write
/// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true.
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeCompressedDelta(bool writeToBitstream, bool &currentValue, bool lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned char &currentValue, unsigned char lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, char &currentValue, char lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned short &currentValue, unsigned short lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, short &currentValue, short lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned int &currentValue, unsigned int lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, int &currentValue, int lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned long &currentValue, unsigned long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, long long &currentValue, long long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned long long &currentValue, unsigned long long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, float &currentValue, float lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, double &currentValue, double lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
bool SerializeCompressedDelta(bool writeToBitstream, long double &currentValue, long double lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;}
/// Save as SerializeCompressedDelta(templateType &currentValue, templateType lastValue) when we have an unknown second parameter
bool SerializeCompressedDelta(bool writeToBitstream, bool &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned char &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, char &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned short &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, short &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned int &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, int &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, long long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, float &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, double &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
bool SerializeCompressedDelta(bool writeToBitstream, long double &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;}
/// Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] input a byte buffer
/// \param[in] numberOfBytes the size of \a input in bytes
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool Serialize(bool writeToBitstream, char* input, const int numberOfBytes );
/// Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors.
/// Accurate to 1/32767.5.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeNormVector(bool writeToBitstream, float &x, float &y, float z ){if (writeToBitstream) WriteNormVector(x,y,z); else return ReadNormVector(x,y,z); return true;}
bool SerializeNormVector(bool writeToBitstream, double &x, double &y, double &z ){if (writeToBitstream) WriteNormVector(x,y,z); else return ReadNormVector(x,y,z); return true;}
/// Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12.
/// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeVector(bool writeToBitstream, float &x, float &y, float &z ){if (writeToBitstream) WriteVector(x,y,z); else return ReadVector(x,y,z); return true;}
bool SerializeVector(bool writeToBitstream, double &x, double &y, double &z ){if (writeToBitstream) WriteVector(x,y,z); else return ReadVector(x,y,z); return true;}
/// Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] w w
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeNormQuat(bool writeToBitstream, float &w, float &x, float &y, float &z){if (writeToBitstream) WriteNormQuat(w,x,y,z); else return ReadNormQuat(w,x,y,z); return true;}
bool SerializeNormQuat(bool writeToBitstream, double &w, double &x, double &y, double &z){if (writeToBitstream) WriteNormQuat(w,x,y,z); else return ReadNormQuat(w,x,y,z); return true;}
/// Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each
/// for 6 bytes instead of 36
/// Lossy, although the result is renormalized
bool SerializeOrthMatrix(
bool writeToBitstream,
float &m00, float &m01, float &m02,
float &m10, float &m11, float &m12,
float &m20, float &m21, float &m22 ){if (writeToBitstream) WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); else return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); return true;}
bool SerializeOrthMatrix(
bool writeToBitstream,
double &m00, double &m01, double &m02,
double &m10, double &m11, double &m12,
double &m20, double &m21, double &m22 ){if (writeToBitstream) WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); else return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); return true;}
/// Bidirectional serialize/deserialize numberToSerialize bits to/from the input. Right aligned
/// data means in the case of a partial byte, the bits are aligned
/// from the right (bit 0) rather than the left (as in the normal
/// internal representation) You would set this to true when
/// writing user data, and false when copying bitstream data, such
/// as writing one bitstream to another
/// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data
/// \param[in] input The data
/// \param[in] numberOfBitsToSerialize The number of bits to write
/// \param[in] rightAlignedBits if true data will be right aligned
/// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful.
bool SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits = true );
/// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
/// \param[in] var The value to write
void Write(bool var){if ( var ) Write1(); else Write0();}
void Write(unsigned char var){WriteBits( ( unsigned char* ) & var, sizeof( unsigned char ) * 8, true );}
void Write(char var){WriteBits( ( unsigned char* ) & var, sizeof( char ) * 8, true );}
void Write(unsigned short var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned short)); WriteBits( ( unsigned char* ) output, sizeof(unsigned short) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );}
void Write(short var) {if (DoEndianSwap()){unsigned char output[sizeof(short)]; ReverseBytes((unsigned char*)&var, output, sizeof(short)); WriteBits( ( unsigned char* ) output, sizeof(short) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(short) * 8, true );}
void Write(unsigned int var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned int)); WriteBits( ( unsigned char* ) output, sizeof(unsigned int) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );}
void Write(int var) {if (DoEndianSwap()){unsigned char output[sizeof(int)]; ReverseBytes((unsigned char*)&var, output, sizeof(int)); WriteBits( ( unsigned char* ) output, sizeof(int) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(int) * 8, true );}
void Write(unsigned long var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long)); WriteBits( ( unsigned char* ) output, sizeof(unsigned long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );}
void Write(long var) {if (DoEndianSwap()){unsigned char output[sizeof(long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long)); WriteBits( ( unsigned char* ) output, sizeof(long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long) * 8, true );}
void Write(long long var) {if (DoEndianSwap()){unsigned char output[sizeof(long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long long)); WriteBits( ( unsigned char* ) output, sizeof(long long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long long) * 8, true );}
void Write(unsigned long long var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long long)); WriteBits( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );}
void Write(float var) {if (DoEndianSwap()){unsigned char output[sizeof(float)]; ReverseBytes((unsigned char*)&var, output, sizeof(float)); WriteBits( ( unsigned char* ) output, sizeof(float) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(float) * 8, true );}
void Write(double var) {if (DoEndianSwap()){unsigned char output[sizeof(double)]; ReverseBytes((unsigned char*)&var, output, sizeof(double)); WriteBits( ( unsigned char* ) output, sizeof(double) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(double) * 8, true );}
void Write(long double var) {if (DoEndianSwap()){unsigned char output[sizeof(long double)]; ReverseBytes((unsigned char*)&var, output, sizeof(long double)); WriteBits( ( unsigned char* ) output, sizeof(long double) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long double) * 8, true );}
void Write(void* var) {if (DoEndianSwap()){unsigned char output[sizeof(void*)]; ReverseBytes((unsigned char*)&var, output, sizeof(void*)); WriteBits( ( unsigned char* ) output, sizeof(void*) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(void*) * 8, true );}
void Write(SystemAddress var){WriteBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); Write(var.port);}
void Write(NetworkID var){if (NetworkID::IsPeerToPeerMode()) Write(var.systemAddress); Write(var.localSystemAddress);}
/// Write any integral type to a bitstream. If the current value is different from the last value
/// the current value will be written. Otherwise, a single bit will be written
/// \param[in] currentValue The current value to write
/// \param[in] lastValue The last value to compare against
void WriteDelta(bool currentValue, bool lastValue){
#pragma warning(disable:4100) // warning C4100: 'peer' : unreferenced formal parameter
Write(currentValue);
}
void WriteDelta(unsigned char currentValue, unsigned char lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(char currentValue, char lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(unsigned short currentValue, unsigned short lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(short currentValue, short lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(unsigned int currentValue, unsigned int lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(int currentValue, int lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(unsigned long currentValue, unsigned long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(long currentValue, long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(long long currentValue, long long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(unsigned long long currentValue, unsigned long long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(float currentValue, float lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(double currentValue, double lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(long double currentValue, long double lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(SystemAddress currentValue, SystemAddress lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
void WriteDelta(NetworkID currentValue, NetworkID lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}}
/// WriteDelta when you don't know what the last value is, or there is no last value.
/// \param[in] currentValue The current value to write
void WriteDelta(bool var){Write(var);}
void WriteDelta(unsigned char var){Write(true); Write(var);}
void WriteDelta(char var){Write(true); Write(var);}
void WriteDelta(unsigned short var){Write(true); Write(var);}
void WriteDelta(short var){Write(true); Write(var);}
void WriteDelta(unsigned int var){Write(true); Write(var);}
void WriteDelta(int var){Write(true); Write(var);}
void WriteDelta(unsigned long var){Write(true); Write(var);}
void WriteDelta(long var){Write(true); Write(var);}
void WriteDelta(long long var){Write(true); Write(var);}
void WriteDelta(unsigned long long var){Write(true); Write(var);}
void WriteDelta(float var){Write(true); Write(var);}
void WriteDelta(double var){Write(true); Write(var);}
void WriteDelta(long double var){Write(true); Write(var);}
void WriteDelta(SystemAddress var){Write(true); Write(var);}
void WriteDelta(NetworkID var){Write(true); Write(var);}
/// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// \param[in] var The value to write
void WriteCompressed(bool var) {Write(var);}
void WriteCompressed(unsigned char var) {WriteCompressed( ( unsigned char* ) & var, sizeof( unsigned char ) * 8, true );}
void WriteCompressed(char var) {WriteCompressed( (unsigned char* ) & var, sizeof( unsigned char ) * 8, true );}
void WriteCompressed(unsigned short var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned short)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned short)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned short) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );}
void WriteCompressed(short var) {if (DoEndianSwap()) {unsigned char output[sizeof(short)]; ReverseBytes((unsigned char*)&var, output, sizeof(short)); WriteCompressed( ( unsigned char* ) output, sizeof(short) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(short) * 8, true );}
void WriteCompressed(unsigned int var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned int)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned int)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned int) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );}
void WriteCompressed(int var) {if (DoEndianSwap()) { unsigned char output[sizeof(int)]; ReverseBytes((unsigned char*)&var, output, sizeof(int)); WriteCompressed( ( unsigned char* ) output, sizeof(int) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(int) * 8, true );}
void WriteCompressed(unsigned long var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );}
void WriteCompressed(long var) {if (DoEndianSwap()) {unsigned char output[sizeof(long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long)); WriteCompressed( ( unsigned char* ) output, sizeof(long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(long) * 8, true );}
void WriteCompressed(long long var) {if (DoEndianSwap()) {unsigned char output[sizeof(long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long long)); WriteCompressed( ( unsigned char* ) output, sizeof(long long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(long long) * 8, true );}
void WriteCompressed(unsigned long long var) {if (DoEndianSwap()) { unsigned char output[sizeof(unsigned long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long long)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );}
void WriteCompressed(float var) {assert(var > -1.01f && var < 1.01f); if (var < -1.0f) var=-1.0f; if (var > 1.0f) var=1.0f; Write((unsigned short)((var+1.0f)*32767.5f));}
void WriteCompressed(double var) {assert(var > -1.01 && var < 1.01); if (var < -1.0) var=-1.0; if (var > 1.0) var=1.0; Write((unsigned long)((var+1.0)*2147483648.0));}
void WriteCompressed(long double var) {assert(var > -1.01 && var < 1.01); if (var < -1.0) var=-1.0; if (var > 1.0) var=1.0; Write((unsigned long)((var+1.0)*2147483648.0));}
/// Write any integral type to a bitstream. If the current value is different from the last value
/// the current value will be written. Otherwise, a single bit will be written
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// \param[in] currentValue The current value to write
/// \param[in] lastValue The last value to compare against
void WriteCompressedDelta(bool currentValue, bool lastValue)
{
#pragma warning(disable:4100) // warning C4100: 'peer' : unreferenced formal parameter
Write(currentValue);
}
void WriteCompressedDelta(unsigned char currentValue, unsigned char lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(char currentValue, char lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(unsigned short currentValue, unsigned short lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(short currentValue, short lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(unsigned int currentValue, unsigned int lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(int currentValue, int lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(unsigned long currentValue, unsigned long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(long currentValue, long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(long long currentValue, long long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(unsigned long long currentValue, unsigned long long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(float currentValue, float lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(double currentValue, double lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
void WriteCompressedDelta(long double currentValue, long double lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}}
/// Save as WriteCompressedDelta(templateType currentValue, templateType lastValue) when we have an unknown second parameter
void WriteCompressedDelta(bool var) {Write(var);}
void WriteCompressedDelta(unsigned char var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(char var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(unsigned short var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(short var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(unsigned int var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(int var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(unsigned long var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(long var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(long long var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(unsigned long long var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(float var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(double var) { Write(true); WriteCompressed(var); }
void WriteCompressedDelta(long double var) { Write(true); WriteCompressed(var); }
/// Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping.
/// \param[in] var The value to read
bool Read(bool &var){if ( readOffset + 1 > numberOfBitsUsed ) return false;
if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) )
var = true;
else
var = false;
// Has to be on a different line for Mac
readOffset++;
return true;
}
bool Read(unsigned char &var) {return ReadBits( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );}
bool Read(char &var) {return ReadBits( ( unsigned char* ) &var, sizeof(char) * 8, true );}
bool Read(unsigned short &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned short) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned short)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );}
bool Read(short &var) {if (DoEndianSwap()){unsigned char output[sizeof(short)]; if (ReadBits( ( unsigned char* ) output, sizeof(short) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(short)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(short) * 8, true );}
bool Read(unsigned int &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned int) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned int)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );}
bool Read(int &var) {if (DoEndianSwap()){unsigned char output[sizeof(int)]; if (ReadBits( ( unsigned char* ) output, sizeof(int) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(int)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(int) * 8, true );}
bool Read(unsigned long &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );}
bool Read(long &var) {if (DoEndianSwap()){unsigned char output[sizeof(long)]; if (ReadBits( ( unsigned char* ) output, sizeof(long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long) * 8, true );}
bool Read(long long &var) {if (DoEndianSwap()){unsigned char output[sizeof(long long)]; if (ReadBits( ( unsigned char* ) output, sizeof(long long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long long) * 8, true );}
bool Read(unsigned long long &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );}
bool Read(float &var) {if (DoEndianSwap()){unsigned char output[sizeof(float)]; if (ReadBits( ( unsigned char* ) output, sizeof(float) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(float)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(float) * 8, true );}
bool Read(double &var) {if (DoEndianSwap()){unsigned char output[sizeof(double)]; if (ReadBits( ( unsigned char* ) output, sizeof(double) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(double)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(double) * 8, true );}
bool Read(long double &var) {if (DoEndianSwap()){unsigned char output[sizeof(long double)]; if (ReadBits( ( unsigned char* ) output, sizeof(long double) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long double)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long double) * 8, true );}
bool Read(void* &var) {if (DoEndianSwap()){unsigned char output[sizeof(void*)]; if (ReadBits( ( unsigned char* ) output, sizeof(void*) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(void*)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(void*) * 8, true );}
bool Read(SystemAddress &var){ReadBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); return Read(var.port);}
bool Read(NetworkID &var){if (NetworkID::IsPeerToPeerMode()) Read(var.systemAddress); return Read(var.localSystemAddress);}
/// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function,
/// var will be updated. Otherwise it will retain the current value.
/// ReadDelta is only valid from a previous call to WriteDelta
/// \param[in] var The value to read
bool ReadDelta(bool &var) {return Read(var);}
bool ReadDelta(unsigned char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(unsigned short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(unsigned int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(unsigned long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(unsigned long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(float &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(long double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(SystemAddress &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
bool ReadDelta(NetworkID &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;}
/// Read any integral type from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping.
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// \param[in] var The value to read
bool ReadCompressed(bool &var) {return Read(var);}
bool ReadCompressed(unsigned char &var) {return ReadCompressed( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );}
bool ReadCompressed(char &var) {return ReadCompressed( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );}
bool ReadCompressed(unsigned short &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned short) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned short)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );}
bool ReadCompressed(short &var){if (DoEndianSwap()){unsigned char output[sizeof(short)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(short) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(short)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(short) * 8, true );}
bool ReadCompressed(unsigned int &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned int) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned int)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );}
bool ReadCompressed(int &var){if (DoEndianSwap()){unsigned char output[sizeof(int)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(int) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(int)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(int) * 8, true );}
bool ReadCompressed(unsigned long &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );}
bool ReadCompressed(long &var){if (DoEndianSwap()){unsigned char output[sizeof(long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(long) * 8, true );}
bool ReadCompressed(long long &var){if (DoEndianSwap()){unsigned char output[sizeof(long long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(long long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(long long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(long long) * 8, true );}
bool ReadCompressed(unsigned long long &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );}
bool ReadCompressed(float &var){unsigned short compressedFloat; if (Read(compressedFloat)) { var = ((float)compressedFloat / 32767.5f - 1.0f); return true;} return false;}
bool ReadCompressed(double &var) {unsigned long compressedFloat; if (Read(compressedFloat)) { var = ((double)compressedFloat / 2147483648.0 - 1.0); return true; } return false;}
bool ReadCompressed(long double &var) {unsigned long compressedFloat; if (Read(compressedFloat)) { var = ((long double)compressedFloat / 2147483648.0 - 1.0); return true; } return false;}
bool ReadCompressed(SystemAddress &var) {return Read(var);}
bool ReadCompressed(NetworkID &var) {return Read(var);}
/// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function,
/// var will be updated. Otherwise it will retain the current value.
/// the current value will be updated.
/// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1.
/// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type
/// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte
/// ReadCompressedDelta is only valid from a previous call to WriteDelta
/// \param[in] var The value to read
bool ReadCompressedDelta(bool &var) {return Read(var);}
bool ReadCompressedDelta(unsigned char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(unsigned short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(unsigned int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(unsigned long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(unsigned long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(float &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
bool ReadCompressedDelta(long double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;}
/// Write an array or casted stream or raw data. This does NOT do endian swapping.
/// \param[in] input a byte buffer
/// \param[in] numberOfBytes the size of \a input in bytes
void Write( const char* input, const int numberOfBytes );
/// Write one bitstream to another
/// \param[in] numberOfBits bits to write
/// \param bitStream the bitstream to copy from
void Write( BitStream *bitStream, int numberOfBits );
void Write( BitStream *bitStream );
/// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors.
/// Accurate to 1/32767.5.
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
void WriteNormVector( float x, float y, float z );
void WriteNormVector( double x, double y, double z ) {WriteNormVector((float)x,(float)y,(float)z);}
/// Write a vector, using 10 bytes instead of 12.
/// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important.
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
void WriteVector( float x, float y, float z );
void WriteVector( double x, double y, double z ) {WriteVector((float)x, (float)y, (float)z);}
/// Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
/// \param[in] w w
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
void WriteNormQuat( float w, float x, float y, float z);
void WriteNormQuat( double w, double x, double y, double z) {WriteNormQuat((float)w, (float) x, (float) y, (float) z);}
/// Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each
/// for 6 bytes instead of 36
/// Lossy, although the result is renormalized
void WriteOrthMatrix(
float m00, float m01, float m02,
float m10, float m11, float m12,
float m20, float m21, float m22 )
{
WriteOrthMatrix((double)m00,(double)m01,(double)m02,
(double)m10,(double)m11,(double)m12,
(double)m20,(double)m21,(double)m22);
}
void WriteOrthMatrix(
double m00, double m01, double m02,
double m10, double m11, double m12,
double m20, double m21, double m22 );
/// Read an array or casted stream of byte. The array
/// is raw data. There is no automatic endian conversion with this function
/// \param[in] output The result byte array. It should be larger than @em numberOfBytes.
/// \param[in] numberOfBytes The number of byte to read
/// \return true on success false if there is some missing bytes.
bool Read( char* output, const int numberOfBytes );
/// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors.
/// Accurate to 1/32767.5.
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
bool ReadNormVector( float &x, float &y, float &z );
bool ReadNormVector( double &x, double &y, double &z ) {float fx, fy, fz; bool b = ReadNormVector(fx, fy, fz); x=fx; y=fy; z=fz; return b;}
/// Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector
/// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important.
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
bool ReadVector( float x, float y, float z );
bool ReadVector( double &x, double &y, double &z ) {return ReadVector((float)x, (float)y, (float)z);}
/// Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes.
/// \param[in] w w
/// \param[in] x x
/// \param[in] y y
/// \param[in] z z
bool ReadNormQuat( float &w, float &x, float &y, float &z){double dw, dx, dy, dz; bool b=ReadNormQuat(dw, dx, dy, dz); w=(float)dw; x=(float)dx; y=(float)dy; z=(float)dz; return b;}
bool ReadNormQuat( double &w, double &x, double &y, double &z);
/// Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolating the 4th.
/// for 6 bytes instead of 36
/// Lossy, although the result is renormalized
bool ReadOrthMatrix(
float &m00, float &m01, float &m02,
float &m10, float &m11, float &m12,
float &m20, float &m21, float &m22 );
bool ReadOrthMatrix(
double &m00, double &m01, double &m02,
double &m10, double &m11, double &m12,
double &m20, double &m21, double &m22 );
///Sets the read pointer back to the beginning of your data.
void ResetReadPointer( void );
/// Sets the write pointer back to the beginning of your data.
void ResetWritePointer( void );
///This is good to call when you are done with the stream to make
/// sure you didn't leave any data left over void
void AssertStreamEmpty( void );
/// printf the bits in the stream. Great for debugging.
void PrintBits( void ) const;
/// Ignore data we don't intend to read
/// \param[in] numberOfBits The number of bits to ignore
void IgnoreBits( const int numberOfBits );
/// Ignore data we don't intend to read
/// \param[in] numberOfBits The number of bytes to ignore
void IgnoreBytes( const int numberOfBytes );
///Move the write pointer to a position on the array.
/// \param[in] offset the offset from the start of the array.
/// \attention
/// Dangerous if you don't know what you are doing!
/// For efficiency reasons you can only write mid-stream if your data is byte aligned.
void SetWriteOffset( const int offset );
/// Returns the length in bits of the stream
inline int GetNumberOfBitsUsed( void ) const {return GetWriteOffset();}
inline int GetWriteOffset( void ) const {return numberOfBitsUsed;}
///Returns the length in bytes of the stream
inline int GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );}
///Returns the number of bits into the stream that we have read
inline int GetReadOffset( void ) const {return readOffset;}
// Sets the read bit index
inline void SetReadOffset( int newReadOffset ) {readOffset=newReadOffset;}
///Returns the number of bits left in the stream that haven't been read
inline int GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;}
/// Makes a copy of the internal data for you \a _data will point to
/// the stream. Returns the length in bits of the stream. Partial
/// bytes are left aligned
/// \param[out] _data The allocated copy of GetData()
int CopyData( unsigned char** _data ) const;
/// Set the stream to some initial data.
/// \internal
void SetData( unsigned char *input );
/// Gets the data that BitStream is writing to / reading from
/// Partial bytes are left aligned.
/// \return A pointer to the internal state
inline unsigned char* GetData( void ) const {return data;}
/// Write numberToWrite bits from the input source Right aligned
/// data means in the case of a partial byte, the bits are aligned
/// from the right (bit 0) rather than the left (as in the normal
/// internal representation) You would set this to true when
/// writing user data, and false when copying bitstream data, such
/// as writing one bitstream to another
/// \param[in] input The data
/// \param[in] numberOfBitsToWrite The number of bits to write
/// \param[in] rightAlignedBits if true data will be right aligned
void WriteBits( const unsigned char* input, int numberOfBitsToWrite, const bool rightAlignedBits = true );
/// Align the bitstream to the byte boundary and then write the
/// specified number of bits. This is faster than WriteBits but
/// wastes the bits to do the alignment and requires you to call
/// ReadAlignedBits at the corresponding read position.
/// \param[in] input The data
/// \param[in] numberOfBytesToWrite The size of input.
void WriteAlignedBytes( void *input, const int numberOfBytesToWrite );
/// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite
/// \param[in] input The data
/// \param[in] inputLength The size of input.
/// \param[in] maxBytesToWrite Max bytes to write
void WriteAlignedBytesSafe( void *input, const int inputLength, const int maxBytesToWrite );
/// Read bits, starting at the next aligned bits. Note that the
/// modulus 8 starting offset of the sequence must be the same as
/// was used with WriteBits. This will be a problem with packet
/// coalescence unless you byte align the coalesced packets.
/// \param[in] output The byte array larger than @em numberOfBytesToRead
/// \param[in] numberOfBytesToRead The number of byte to read from the internal state
/// \return true if there is enough byte.
bool ReadAlignedBytes( void *output, const int numberOfBytesToRead );
/// Reads what was written by WriteAlignedBytesSafe
/// \param[in] input The data
/// \param[in] maxBytesToRead Maximum number of bytes to read
bool ReadAlignedBytesSafe( void *input, int &inputLength, const int maxBytesToRead );
/// Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to
/// \param[in] input input will be deleted if it is not a pointer to 0
bool ReadAlignedBytesSafeAlloc( char **input, int &inputLength, const int maxBytesToRead );
/// Align the next write and/or read to a byte boundary. This can
/// be used to 'waste' bits to byte align for efficiency reasons It
/// can also be used to force coalesced bitstreams to start on byte
/// boundaries so so WriteAlignedBits and ReadAlignedBits both
/// calculate the same offset when aligning.
void AlignWriteToByteBoundary( void );
/// Align the next write and/or read to a byte boundary. This can
/// be used to 'waste' bits to byte align for efficiency reasons It
/// can also be used to force coalesced bitstreams to start on byte
/// boundaries so so WriteAlignedBits and ReadAlignedBits both
/// calculate the same offset when aligning.
void AlignReadToByteBoundary( void );
/// Read \a numberOfBitsToRead bits to the output source
/// alignBitsToRight should be set to true to convert internal
/// bitstream data to userdata. It should be false if you used
/// WriteBits with rightAlignedBits false
/// \param[in] output The resulting bits array
/// \param[in] numberOfBitsToRead The number of bits to read
/// \param[in] alignBitsToRight if true bits will be right aligned.
/// \return true if there is enough bits to read
bool ReadBits( unsigned char *output, int numberOfBitsToRead, const bool alignBitsToRight = true );
/// Write a 0
void Write0( void );
/// Write a 1
void Write1( void );
/// Reads 1 bit and returns true if that bit is 1 and false if it is 0
bool ReadBit( void );
/// If we used the constructor version with copy data off, this
/// *makes sure it is set to on and the data pointed to is copied.
void AssertCopyData( void );
/// Use this if you pass a pointer copy to the constructor
/// *(_copyData==false) and want to overallocate to prevent
/// *reallocation
void SetNumberOfBitsAllocated( const unsigned int lengthInBits );
/// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite
void AddBitsAndReallocate( const int numberOfBitsToWrite );
/// \internal
/// \return How many bits have been allocated internally
unsigned int GetNumberOfBitsAllocated(void) const;
static bool DoEndianSwap(void);
static bool IsBigEndian(void);
static bool IsNetworkOrder(void);
static void ReverseBytes(unsigned char *input, unsigned char *output, int length);
static void ReverseBytesInPlace(unsigned char *data, int length);
private:
BitStream( const BitStream &invalid) {
#ifdef _MSC_VER
#pragma warning(disable:4100)
// warning C4100: 'invalid' : unreferenced formal parameter
#endif
}
/// Assume the input source points to a native type, compress and write it.
void WriteCompressed( const unsigned char* input, const int size, const bool unsignedData );
/// Assume the input source points to a compressed native type. Decompress and read it.
bool ReadCompressed( unsigned char* output, const int size, const bool unsignedData );
int numberOfBitsUsed;
int numberOfBitsAllocated;
int readOffset;
unsigned char *data;
/// true if the internal buffer is copy of the data passed to the constructor
bool copyData;
/// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded
unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE];
};
inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits )
{
if (writeToBitstream)
WriteBits(input,numberOfBitsToSerialize,rightAlignedBits);
else
return ReadBits(input,numberOfBitsToSerialize,rightAlignedBits);
return true;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif // VC6
#endif

97
thirdparty/raknet/Source/CheckSum.cpp vendored Normal file
View File

@@ -0,0 +1,97 @@
/**
* @file
* @brief CheckSum implementation from http://www.flounder.com/checksum.htm
*
*/
#include "CheckSum.h"
/****************************************************************************
* CheckSum::add
* Inputs:
* unsigned int d: word to add
* Result: void
*
* Effect:
* Adds the bytes of the unsigned int to the CheckSum
****************************************************************************/
void CheckSum::Add ( unsigned int value )
{
union
{
unsigned int value;
unsigned char bytes[ 4 ];
}
data;
data.value = value;
for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ )
Add ( data.bytes[ i ] )
;
} // CheckSum::add(unsigned int)
/****************************************************************************
* CheckSum::add
* Inputs:
* unsigned short value:
* Result: void
*
* Effect:
* Adds the bytes of the unsigned short value to the CheckSum
****************************************************************************/
void CheckSum::Add ( unsigned short value )
{
union
{
unsigned short value;
unsigned char bytes[ 2 ];
}
data;
data.value = value;
for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ )
Add ( data.bytes[ i ] )
;
} // CheckSum::add(unsigned short)
/****************************************************************************
* CheckSum::add
* Inputs:
* unsigned char value:
* Result: void
*
* Effect:
* Adds the byte to the CheckSum
****************************************************************************/
void CheckSum::Add ( unsigned char value )
{
unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) );
r = ( cipher + r ) * c1 + c2;
sum += cipher;
} // CheckSum::add(unsigned char)
/****************************************************************************
* CheckSum::add
* Inputs:
* LPunsigned char b: pointer to byte array
* unsigned int length: count
* Result: void
*
* Effect:
* Adds the bytes to the CheckSum
****************************************************************************/
void CheckSum::Add ( unsigned char *b, unsigned int length )
{
for ( unsigned int i = 0; i < length; i++ )
Add ( b[ i ] )
;
} // CheckSum::add(LPunsigned char, unsigned int)

54
thirdparty/raknet/Source/CheckSum.h vendored Normal file
View File

@@ -0,0 +1,54 @@
/// \file
/// \brief \b [Internal] Generates and validates checksums
///
/// \note I didn't write this, but took it from http://www.flounder.com/checksum.htm
///
#ifndef __CHECKSUM_H
#define __CHECKSUM_H
#include "RakMemoryOverride.h"
/// Generates and validates checksums
class CheckSum : public RakNet::RakMemoryOverride
{
public:
/// Default constructor
CheckSum()
{
Clear();
}
void Clear()
{
sum = 0;
r = 55665;
c1 = 52845;
c2 = 22719;
}
void Add ( unsigned int w );
void Add ( unsigned short w );
void Add ( unsigned char* b, unsigned int length );
void Add ( unsigned char b );
unsigned int Get ()
{
return sum;
}
protected:
unsigned short r;
unsigned short c1;
unsigned short c2;
unsigned int sum;
};
#endif

View File

@@ -0,0 +1,50 @@
/// \file
/// \brief \b [Internal] Depreciated, back from when I supported IO Completion ports.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __CLIENT_CONTEXT_STRUCT_H
#define __CLIENT_CONTEXT_STRUCT_H
#ifdef _XBOX360
#elif defined(_WIN32)
//#include <windows.h>
#endif
#include "RakNetTypes.h"
#include "MTUSize.h"
class RakPeer;
#ifdef __USE_IO_COMPLETION_PORTS
struct ClientContextStruct
{
HANDLE handle; // The socket, also used as a file handle
};
struct ExtendedOverlappedStruct
{
OVERLAPPED overlapped;
char data[ MAXIMUM_MTU_SIZE ]; // Used to hold data to send
int length; // Length of the actual data to send, always under MAXIMUM_MTU_SIZE
unsigned int binaryAddress;
unsigned short port;
RakPeer *rakPeer;
bool read; // Set to true for reads, false for writes
};
#endif
#endif

View File

@@ -0,0 +1,168 @@
#include "CommandParserInterface.h"
#include "TransportInterface.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
#ifdef _XBOX360
#include "Console1Includes.h"
#elif defined(_WIN32)
// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib
// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
#include "LinuxStrings.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255;
int RegisteredCommandComp( const char* const & key, const RegisteredCommand &data )
{
return _stricmp(key,data.command);
}
CommandParserInterface::CommandParserInterface() {}
CommandParserInterface::~CommandParserInterface() {}
void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength)
{
unsigned strIndex, parameterListIndex;
unsigned strLen;
bool replaceDelineator=true;
strLen = (unsigned) strlen(str);
// Replace every instance of delineator, \n, \r with 0
for (strIndex=0; strIndex < strLen; strIndex++)
{
if (str[strIndex]==delineator && replaceDelineator)
str[strIndex]=0;
if (str[strIndex]=='\n' || str[strIndex]=='\r')
str[strIndex]=0;
if (str[strIndex]==delineatorToggle)
{
str[strIndex]=0;
replaceDelineator=!replaceDelineator;
}
}
// Fill up parameterList starting at each non-0
for (strIndex=0, parameterListIndex=0; strIndex < strLen; )
{
if (str[strIndex]!=0)
{
parameterList[parameterListIndex]=str+strIndex;
parameterListIndex++;
assert(parameterListIndex < parameterListLength);
if (parameterListIndex >= parameterListLength)
break;
strIndex++;
while (str[strIndex]!=0 && strIndex < strLen)
strIndex++;
}
else
strIndex++;
}
parameterList[parameterListIndex]=0;
*numParameters=parameterListIndex;
}
void CommandParserInterface::SendCommandList(TransportInterface *transport, SystemAddress systemAddress)
{
unsigned i;
if (commandList.Size())
{
for (i=0; i < commandList.Size(); i++)
{
transport->Send(systemAddress, "%s", commandList[i].command);
if (i < commandList.Size()-1)
transport->Send(systemAddress, ", ");
}
transport->Send(systemAddress, "\r\n");
}
else
transport->Send(systemAddress, "No registered commands\r\n");
}
void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp)
{
RegisteredCommand rc;
rc.command=command;
rc.commandHelp=commandHelp;
rc.parameterCount=parameterCount;
commandList.Insert( command, rc, true);
}
bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc)
{
bool objectExists;
unsigned index;
index=commandList.GetIndexFromKey(command, &objectExists);
if (objectExists)
*rc=commandList[index];
return objectExists;
}
void CommandParserInterface::OnTransportChange(TransportInterface *transport)
{
(void) transport;
}
void CommandParserInterface::OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport)
{
(void) systemAddress;
(void) transport;
}
void CommandParserInterface::OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport)
{
(void) systemAddress;
(void) transport;
}
void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, SystemAddress systemAddress)
{
if (res)
transport->Send(systemAddress, "%s returned true.\r\n", command);
else
transport->Send(systemAddress, "%s returned false.\r\n", command);
}
void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, SystemAddress systemAddress)
{
transport->Send(systemAddress, "%s returned %i.\r\n", command, res);
}
void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, SystemAddress systemAddress)
{
transport->Send(systemAddress, "Successfully called %s.\r\n", command);
}
void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, SystemAddress systemAddress)
{
transport->Send(systemAddress, "%s returned %s.\r\n", command, res);
}
void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, SystemAddress systemAddress)
{
#if !defined(_XBOX360)
in_addr in;
in.s_addr = systemAddress.binaryAddress;
inet_ntoa( in );
transport->Send(systemAddress, "%s returned %s %i:%i\r\n", command,inet_ntoa( in ),res.binaryAddress, res.port);
#else
transport->Send(systemAddress, "%s returned %i:%i\r\n", command,res.binaryAddress, res.port);
#endif
}
SystemAddress CommandParserInterface::IntegersToSystemAddress(int binaryAddress, int port)
{
SystemAddress systemAddress;
systemAddress.binaryAddress=binaryAddress;
systemAddress.port=(unsigned short)port;
return systemAddress;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,149 @@
/// \file
/// \brief Contains CommandParserInterface , from which you derive custom command parsers
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __COMMAND_PARSER_INTERFACE
#define __COMMAND_PARSER_INTERFACE
#include "RakMemoryOverride.h"
#include "RakNetTypes.h"
#include "DS_OrderedList.h"
#include "Export.h"
class TransportInterface;
/// \internal
/// Contains the information related to one command registered with RegisterCommand()
/// Implemented so I can have an automatic help system via SendCommandList()
struct RAK_DLL_EXPORT RegisteredCommand
{
const char *command;
const char *commandHelp;
unsigned char parameterCount;
};
/// List of commands registered with RegisterCommand()
int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data );
/// CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class.
/// Each CommandParserInterface works at the same time as other interfaces in the system.
/// \brief The interface used by command parsers.
class RAK_DLL_EXPORT CommandParserInterface : public RakNet::RakMemoryOverride
{
public:
CommandParserInterface();
virtual ~CommandParserInterface();
/// You are responsible for overriding this function and returning a static string, which will identifier your parser.
/// This should return a static string
/// \return The name that you return.
virtual const char *GetName(void) const=0;
/// A callback for when \a systemAddress has connected to us.
/// \param[in] systemAddress The player that has connected.
/// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players.
virtual void OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport);
/// A callback for when \a systemAddress has disconnected, either gracefully or forcefully
/// \param[in] systemAddress The player that has disconnected.
/// \param[in] transport The transport interface that sent us this information.
virtual void OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport);
/// A callback for when you are expected to send a brief description of your parser to \a systemAddress
/// \param[in] transport The transport interface we can use to write to
/// \param[in] systemAddress The player that requested help.
virtual void SendHelp(TransportInterface *transport, SystemAddress systemAddress)=0;
/// Given \a command with parameters \a parameterList , do whatever processing you wish.
/// \param[in] command The command to process
/// \param[in] numParameters How many parameters were passed along with the command
/// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on.
/// \param[in] transport The transport interface we can use to write to
/// \param[in] systemAddress The player that sent this command.
/// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing
virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString)=0;
/// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer
/// This is the place to do it
/// \param[in] transport The new TransportInterface
virtual void OnTransportChange(TransportInterface *transport);
/// \internal
/// Scan commandList and return the associated array
/// \param[in] command The string to find
/// \param[out] rc Contains the result of this operation
/// \return True if we found the command, false otherwise
virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc);
/// \internal
/// Goes through str, replacing the delineating character with 0's.
/// \param[in] str The string sent by the transport interface
/// \param[in] delineator The character to scan for to use as a delineator
/// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off
/// \param[out] numParameters How many pointers were written to \a parameterList
/// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str
/// \param[in] parameterListLength How big the \a parameterList array is
static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength);
/// \internal
/// Goes through the variable commandList and sends the command portion of each struct
/// \param[in] transport The transport interface we can use to write to
/// \param[in] systemAddress The player to write to
virtual void SendCommandList(TransportInterface *transport, SystemAddress systemAddress);
static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS;
protected:
// Currently only takes static strings - doesn't make a copy of what you pass.
// parameterCount is the number of parameters that the sender has to include with the command.
// Pass 255 to parameterCount to indicate variable number of parameters
/// Registers a command.
/// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS
/// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string.
/// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string.
virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp);
/// Just writes a string to the remote system based on the result ( \a res )of your operation
/// This is not necessary to call, but makes it easier to return results of function calls
/// \param[in] res The result to write
/// \param[in] command The command that this result came from
/// \param[in] transport The transport interface that will be written to
/// \param[in] systemAddress The player this result will be sent to
virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, SystemAddress systemAddress);
virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, SystemAddress systemAddress);
virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, SystemAddress systemAddress);
virtual void ReturnResult(int res, const char *command,TransportInterface *transport, SystemAddress systemAddress);
/// Just writes a string to the remote system when you are calling a function that has no return value
/// This is not necessary to call, but makes it easier to return results of function calls
/// \param[in] res The result to write
/// \param[in] command The command that this result came from
/// \param[in] transport The transport interface that will be written to
/// \param[in] systemAddress The player this result will be sent to
virtual void ReturnResult(const char *command,TransportInterface *transport, SystemAddress systemAddress);
/// Since there's no way to specify a systemAddress directly, the user needs to specify both the binary address and port.
/// Given those parameters, this returns the corresponding SystemAddress
/// \param[in] binaryAddress The binaryAddress portion of SystemAddress
/// \param[in] port The port portion of SystemAddress
SystemAddress IntegersToSystemAddress(int binaryAddress, int port);
DataStructures::OrderedList<const char*, RegisteredCommand, RegisteredCommandComp> commandList;
};
#endif

View File

@@ -0,0 +1,617 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "ConnectionGraph.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include "BitStream.h"
#include "StringCompressor.h"
#include "GetTime.h"
#include <string.h>
#include "RakAssert.h"
#include "SHA1.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
static const int connectionGraphChannel=31;
ConnectionGraph::SystemAddressAndGroupId::SystemAddressAndGroupId()
{
}
ConnectionGraph::SystemAddressAndGroupId::~SystemAddressAndGroupId()
{
}
ConnectionGraph::SystemAddressAndGroupId::SystemAddressAndGroupId(SystemAddress _systemAddress, ConnectionGraphGroupID _groupID)
{
systemAddress=_systemAddress;
groupId=_groupID;
}
bool ConnectionGraph::SystemAddressAndGroupId::operator==( const ConnectionGraph::SystemAddressAndGroupId& right ) const
{
return systemAddress==right.systemAddress;
}
bool ConnectionGraph::SystemAddressAndGroupId::operator!=( const ConnectionGraph::SystemAddressAndGroupId& right ) const
{
return systemAddress!=right.systemAddress;
}
bool ConnectionGraph::SystemAddressAndGroupId::operator > ( const ConnectionGraph::SystemAddressAndGroupId& right ) const
{
return systemAddress>right.systemAddress;
}
bool ConnectionGraph::SystemAddressAndGroupId::operator < ( const ConnectionGraph::SystemAddressAndGroupId& right ) const
{
return systemAddress<right.systemAddress;
}
ConnectionGraph::ConnectionGraph()
{
pw=0;
myGroupId=0;
autoAddNewConnections=true;
// forceBroadcastTime=0;
DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<SystemAddress, SystemAddress>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<ConnectionGraph::SystemAddressAndGroupId, ConnectionGraph::SystemAddressAndGroupId>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<ConnectionGraphGroupID, ConnectionGraphGroupID>::IMPLEMENT_DEFAULT_COMPARISON();
subscribedGroups.Insert(0,0, true);
}
ConnectionGraph::~ConnectionGraph()
{
if (pw)
delete [] pw;
}
void ConnectionGraph::SetPassword(const char *password)
{
if (pw)
{
delete [] pw;
pw=0;
}
if (password && password[0])
{
assert(strlen(password)<256);
pw=(char*) rakMalloc( strlen(password)+1 );
strcpy(pw, password);
}
}
DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false> *ConnectionGraph::GetGraph(void)
{
return &graph;
}
void ConnectionGraph::SetAutoAddNewConnections(bool autoAdd)
{
autoAddNewConnections=autoAdd;
}
void ConnectionGraph::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
graph.Clear();
participantList.Clear();
// forceBroadcastTime=0;
}
void ConnectionGraph::Update(RakPeerInterface *peer)
{
(void) peer;
// RakNetTime time = RakNet::GetTime();
// If the time is past the next weight update time, then refresh all pings of all connected participants and send these out if substantially different.
// if (forceBroadcastTime && time > forceBroadcastTime)
// {
// DataStructures::OrderedList<SystemAddress,SystemAddress> none;
// BroadcastGraphUpdate(none, peer);
// forceBroadcastTime=0;
// }
}
PluginReceiveResult ConnectionGraph::OnReceive(RakPeerInterface *peer, Packet *packet)
{
switch (packet->data[0])
{
case ID_NEW_INCOMING_CONNECTION:
OnNewIncomingConnection(peer, packet);
return RR_CONTINUE_PROCESSING;
case ID_CONNECTION_REQUEST_ACCEPTED:
OnConnectionRequestAccepted(peer, packet);
return RR_CONTINUE_PROCESSING;
case ID_CONNECTION_GRAPH_REQUEST:
OnConnectionGraphRequest(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_CONNECTION_GRAPH_REPLY:
OnConnectionGraphReply(peer, packet);
return RR_CONTINUE_PROCESSING;
case ID_CONNECTION_GRAPH_UPDATE:
OnConnectionGraphUpdate(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_CONNECTION_GRAPH_NEW_CONNECTION:
OnNewConnection(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
// Remove connection lost
case ID_CONNECTION_GRAPH_CONNECTION_LOST:
case ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION:
if (OnConnectionLost(peer, packet, packet->data[0]))
{
if (packet->data[0]==ID_CONNECTION_GRAPH_CONNECTION_LOST)
packet->data[0]=ID_REMOTE_CONNECTION_LOST;
else
packet->data[0]=ID_REMOTE_DISCONNECTION_NOTIFICATION;
return RR_CONTINUE_PROCESSING; // Return this packet to the user
}
return RR_STOP_PROCESSING_AND_DEALLOCATE;
// Local connection lost
case ID_CONNECTION_LOST:
case ID_DISCONNECTION_NOTIFICATION:
{
unsigned char packetId;
// Change to remote connection lost and relay the message
if (packet->data[0]==ID_CONNECTION_LOST)
packetId=ID_CONNECTION_GRAPH_CONNECTION_LOST;
else
packetId=ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION;
HandleDroppedConnection(peer, packet->systemAddress, packetId);
}
}
return RR_CONTINUE_PROCESSING;
}
void ConnectionGraph::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
{
HandleDroppedConnection(peer, systemAddress, ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION);
}
void ConnectionGraph::HandleDroppedConnection(RakPeerInterface *peer, SystemAddress systemAddress, unsigned char packetId)
{
assert(peer);
RemoveParticipant(systemAddress);
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
RemoveAndRelayConnection(ignoreList, packetId, systemAddress, peer->GetExternalID(systemAddress), peer);
}
void ConnectionGraph::OnNewIncomingConnection(RakPeerInterface *peer, Packet *packet)
{
if (autoAddNewConnections==false)
return;
// 0 is the default groupId
AddNewConnection(peer, packet->systemAddress, 0);
}
void ConnectionGraph::OnConnectionRequestAccepted(RakPeerInterface *peer, Packet *packet)
{
if (autoAddNewConnections==false)
return;
RequestConnectionGraph(peer, packet->systemAddress);
// 0 is the default groupId
AddNewConnection(peer, packet->systemAddress, 0);
}
void ConnectionGraph::SetGroupId(ConnectionGraphGroupID groupId)
{
myGroupId=groupId;
}
void ConnectionGraph::AddNewConnection(RakPeerInterface *peer, SystemAddress systemAddress, ConnectionGraphGroupID groupId)
{
if (autoAddNewConnections==false && subscribedGroups.HasData(groupId)==false)
return;
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
SystemAddressAndGroupId first, second;
first.systemAddress=systemAddress;
first.groupId=groupId;
second.systemAddress=peer->GetExternalID(systemAddress);
second.groupId=myGroupId;
AddAndRelayConnection(ignoreList, first, second, (unsigned short)peer->GetAveragePing(systemAddress), peer);
}
void ConnectionGraph::SubscribeToGroup(ConnectionGraphGroupID groupId)
{
subscribedGroups.Insert(groupId, groupId, true);
}
void ConnectionGraph::UnsubscribeFromGroup(ConnectionGraphGroupID groupId)
{
subscribedGroups.Remove(groupId);
}
void ConnectionGraph::RequestConnectionGraph(RakPeerInterface *peer, SystemAddress systemAddress)
{
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_REQUEST);
stringCompressor->EncodeString(pw,256,&outBitstream);
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, systemAddress, false);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("ID_CONNECTION_GRAPH_REQUEST from %i to %i\n", peer->GetInternalID().port, systemAddress.port);
#endif
}
void ConnectionGraph::OnConnectionGraphRequest(RakPeerInterface *peer, Packet *packet)
{
char password[256];
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
stringCompressor->DecodeString(password,256,&inBitstream);
if (pw && pw[0] && strcmp(pw, password)!=0)
return;
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("ID_CONNECTION_GRAPH_REPLY ");
#endif
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_REPLY);
stringCompressor->EncodeString(pw,256,&outBitstream);
SerializeWeightedGraph(&outBitstream, graph);
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->systemAddress, false);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("from %i to %i\n", peer->GetInternalID().port, packet->systemAddress.port);
#endif
// Add packet->systemAddress to the participant list if it is not already there
AddParticipant(packet->systemAddress);
}
void ConnectionGraph::OnConnectionGraphReply(RakPeerInterface *peer, Packet *packet)
{
unsigned char password[256];
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
stringCompressor->DecodeString((char*)password,256,&inBitstream);
if (pw && pw[0] && strcmp(pw, (const char*)password)!=0)
return;
// Serialize the weighted graph and send it to them
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_UPDATE);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("ID_CONNECTION_GRAPH_UPDATE ");
#endif
// Send our current graph to the sender
SerializeWeightedGraph(&outBitstream, graph);
// Write the systems that have processed this graph so we don't resend to these systems
outBitstream.Write((unsigned short) 1);
outBitstream.Write(peer->GetExternalID(packet->systemAddress));
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("from %i to %i\n", peer->GetInternalID().port, packet->systemAddress.port);
#endif
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->systemAddress, false);
// Add packet->systemAddress to the participant list if it is not already there
AddParticipant(packet->systemAddress);
if (DeserializeWeightedGraph(&inBitstream, peer)==false)
return;
// Forward the updated graph to all current participants
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
ignoreList.Insert(packet->systemAddress,packet->systemAddress, true);
BroadcastGraphUpdate(ignoreList, peer);
}
void ConnectionGraph::OnConnectionGraphUpdate(RakPeerInterface *peer, Packet *packet)
{
// Only accept from participants
if (participantList.HasData(packet->systemAddress)==false)
return;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
if (DeserializeWeightedGraph(&inBitstream, peer)==false)
return;
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
// Forward the updated graph to all participants.
ignoreList.Insert(packet->systemAddress,packet->systemAddress, false);
BroadcastGraphUpdate(ignoreList, peer);
}
void ConnectionGraph::OnNewConnection(RakPeerInterface *peer, Packet *packet)
{
// Only accept from participants
if (participantList.HasData(packet->systemAddress)==false)
return;
SystemAddressAndGroupId node1, node2;
unsigned short ping;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
inBitstream.Read(node1.systemAddress);
inBitstream.Read(node1.groupId);
inBitstream.Read(node2.systemAddress);
inBitstream.Read(node2.groupId);
if (inBitstream.Read(ping)==false)
return;
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
ignoreList.Insert(packet->systemAddress,packet->systemAddress, false);
AddAndRelayConnection(ignoreList, node1, node2, ping, peer);
}
bool ConnectionGraph::OnConnectionLost(RakPeerInterface *peer, Packet *packet, unsigned char packetId)
{
// Only accept from participants
if (participantList.HasData(packet->systemAddress)==false)
return false;
SystemAddress node1, node2;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
// This is correct - group IDs are not written for removal, only addition.
inBitstream.Read(node1);
if (inBitstream.Read(node2)==false)
return false;
DataStructures::OrderedList<SystemAddress,SystemAddress> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
ignoreList.Insert(packet->systemAddress, packet->systemAddress, false);
return RemoveAndRelayConnection(ignoreList, packetId, node1, node2, peer);
}
bool ConnectionGraph::DeserializeIgnoreList(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakNet::BitStream *inBitstream )
{
unsigned short count;
SystemAddress temp;
unsigned i;
inBitstream->Read(count);
for (i=0; i < count; i++)
{
if (inBitstream->Read(temp)==false)
{
assert(0);
return false;
}
ignoreList.Insert(temp,temp, true);
}
return true;
}
void ConnectionGraph::SerializeWeightedGraph(RakNet::BitStream *out, const DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false> &g) const
{
unsigned nodeIndex, connectionIndex;
BitSize_t countOffset, oldOffset;
unsigned short count;
SystemAddressAndGroupId node1, node2;
unsigned short weight;
out->WriteCompressed(g.GetNodeCount());
for (nodeIndex=0; nodeIndex < g.GetNodeCount(); nodeIndex++)
{
// Write the node
node1=g.GetNodeAtIndex(nodeIndex);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("[%i] ", node1.systemAddress.port);
#endif
out->Write(node1.systemAddress);
out->Write(node1.groupId);
// Write the adjacency list count
count=(unsigned short)g.GetConnectionCount(nodeIndex);
out->AlignWriteToByteBoundary();
countOffset=out->GetWriteOffset();
out->Write(count);
count=0;
for (connectionIndex=0; connectionIndex < g.GetConnectionCount(nodeIndex); connectionIndex++)
{
g.GetConnectionAtIndex(nodeIndex, connectionIndex, node2, weight);
// For efficiencies' sake, only serialize the upper half of the connection pairs
if (node2 > node1)
{
count++;
out->Write(node2.systemAddress);
out->Write(node2.groupId);
out->Write(weight);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("(%i) ", node2.systemAddress.port);
#endif
}
}
// Go back and change how many elements were written
oldOffset=out->GetWriteOffset();
out->SetWriteOffset(countOffset);
out->Write(count);
out->SetWriteOffset(oldOffset);
}
}
bool ConnectionGraph::DeserializeWeightedGraph(RakNet::BitStream *inBitstream, RakPeerInterface *peer)
{
unsigned nodeCount, nodeIndex, connectionIndex;
unsigned short connectionCount;
SystemAddressAndGroupId node, connection;
bool anyConnectionsNew=false;
unsigned short weight;
inBitstream->ReadCompressed(nodeCount);
for (nodeIndex=0; nodeIndex < nodeCount; nodeIndex++)
{
inBitstream->Read(node.systemAddress);
inBitstream->Read(node.groupId);
inBitstream->AlignReadToByteBoundary();
if (inBitstream->Read(connectionCount)==false)
{
assert(0);
return false;
}
for (connectionIndex=0; connectionIndex < connectionCount; connectionIndex++)
{
inBitstream->Read(connection.systemAddress);
inBitstream->Read(connection.groupId);
if (inBitstream->Read(weight)==false)
{
assert(0);
return false;
}
if (subscribedGroups.HasData(connection.groupId)==false ||
subscribedGroups.HasData(node.groupId)==false)
continue;
RakAssert(node.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS);
RakAssert(connection.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS);
if (IsNewRemoteConnection(node,connection,peer))
NotifyUserOfRemoteConnection(node,connection,weight,peer);
if (graph.HasConnection(node,connection)==false)
anyConnectionsNew=true;
graph.AddConnection(node,connection,weight);
}
}
return anyConnectionsNew;
}
void ConnectionGraph::RemoveParticipant(SystemAddress systemAddress)
{
unsigned index;
bool objectExists;
index=participantList.GetIndexFromKey(systemAddress, &objectExists);
if (objectExists)
participantList.RemoveAtIndex(index);
}
void ConnectionGraph::AddParticipant(SystemAddress systemAddress)
{
participantList.Insert(systemAddress,systemAddress, false);
}
void ConnectionGraph::AddAndRelayConnection(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2, unsigned short ping, RakPeerInterface *peer)
{
if (graph.HasConnection(conn1,conn2))
return;
if (ping==65535)
ping=0;
assert(conn1.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS);
assert(conn2.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS);
if (IsNewRemoteConnection(conn1,conn2,peer))
{
NotifyUserOfRemoteConnection(conn1,conn2,ping,peer);
// What was this return here for?
// return;
}
graph.AddConnection(conn1,conn2,ping);
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_NEW_CONNECTION);
outBitstream.Write(conn1.systemAddress);
outBitstream.Write(conn1.groupId);
outBitstream.Write(conn2.systemAddress);
outBitstream.Write(conn2.groupId);
outBitstream.Write(ping);
ignoreList.Insert(conn2.systemAddress,conn2.systemAddress, false);
ignoreList.Insert(conn1.systemAddress,conn1.systemAddress, false);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
}
bool ConnectionGraph::RemoveAndRelayConnection(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, unsigned char packetId, const SystemAddress node1, const SystemAddress node2, RakPeerInterface *peer)
{
SystemAddressAndGroupId n1, n2;
n1.systemAddress=node1;
n2.systemAddress=node2;
if (graph.HasConnection(n1,n2)==false)
return false;
graph.RemoveConnection(n1,n2);
RakNet::BitStream outBitstream;
outBitstream.Write(packetId);
outBitstream.Write(node1);
outBitstream.Write(node2);
ignoreList.Insert(node1,node1, false);
ignoreList.Insert(node2,node2, false);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
return true;
}
void ConnectionGraph::BroadcastGraphUpdate(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakPeerInterface *peer)
{
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_CONNECTION_GRAPH_UPDATE);
SerializeWeightedGraph(&outBitstream, graph);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
}
void ConnectionGraph::SerializeIgnoreListAndBroadcast(RakNet::BitStream *outBitstream, DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakPeerInterface *peer)
{
DataStructures::List<SystemAddress> sendList;
unsigned i;
for (i=0; i < participantList.Size(); i++)
{
if (ignoreList.HasData(participantList[i])==false)
sendList.Insert(participantList[i]);
}
if (sendList.Size()==0)
return;
SystemAddress self = peer->GetExternalID(sendList[0]);
ignoreList.Insert(self,self, false);
outBitstream->Write((unsigned short) (ignoreList.Size()+sendList.Size()));
for (i=0; i < ignoreList.Size(); i++)
outBitstream->Write(ignoreList[i]);
for (i=0; i < sendList.Size(); i++)
outBitstream->Write(sendList[i]);
for (i=0; i < sendList.Size(); i++)
{
peer->Send(outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, sendList[i], false);
}
}
bool ConnectionGraph::IsNewRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,RakPeerInterface *peer)
{
if (graph.HasConnection(conn1,conn2)==false &&
subscribedGroups.HasData(conn1.groupId) &&
subscribedGroups.HasData(conn2.groupId) &&
(peer->IsConnected(conn1.systemAddress)==false || peer->IsConnected(conn2.systemAddress)==false))
{
SystemAddress externalId1, externalId2;
externalId1=peer->GetExternalID(conn1.systemAddress);
externalId2=peer->GetExternalID(conn2.systemAddress);
return (externalId1!=conn1.systemAddress && externalId1!=conn2.systemAddress &&
externalId2!=conn1.systemAddress && externalId2!=conn2.systemAddress);
}
return false;
}
void ConnectionGraph::NotifyUserOfRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,unsigned short ping, RakPeerInterface *peer)
{
// Create a packet to tell the user of this event
static const int length=sizeof(MessageID) + (sizeof(SystemAddress) + sizeof(ConnectionGraphGroupID)) * 2 + sizeof(unsigned short);
Packet *p = peer->AllocatePacket(length);
RakNet::BitStream b(p->data, length, false);
p->bitSize=p->length*8;
b.SetWriteOffset(0);
b.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION);
b.Write(conn1.systemAddress);
b.Write(conn1.groupId);
b.Write(conn2.systemAddress);
b.Write(conn2.groupId);
b.Write(ping);
if (peer->IsConnected(conn2.systemAddress)==false)
p->systemAddress=conn2.systemAddress;
else
p->systemAddress=conn1.systemAddress;
peer->PushBackPacket(p, false);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,166 @@
/// \file
/// \brief Connection graph plugin. This maintains a graph of connections for the entire network, so every peer knows about every other peer.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __CONNECTION_GRAPH_H
#define __CONNECTION_GRAPH_H
class RakPeerInterface;
#include "RakMemoryOverride.h"
#include "RakNetTypes.h"
#include "PluginInterface.h"
#include "DS_List.h"
#include "DS_WeightedGraph.h"
#include "GetTime.h"
#include "Export.h"
// If you need more than 255 groups just change this typedef
typedef unsigned char ConnectionGraphGroupID;
/// \defgroup CONNECTION_GRAPH_GROUP ConnectionGraph
/// \ingroup PLUGINS_GROUP
/// \ingroup CONNECTION_GRAPH_GROUP
/// \brief A connection graph. Each peer will know about all other peers.
class RAK_DLL_EXPORT ConnectionGraph : public PluginInterface
{
public:
ConnectionGraph();
virtual ~ConnectionGraph();
/// A node in the connection graph
struct RAK_DLL_EXPORT SystemAddressAndGroupId
{
SystemAddressAndGroupId();
~SystemAddressAndGroupId();
SystemAddressAndGroupId(SystemAddress _systemAddress, ConnectionGraphGroupID _groupID);
SystemAddress systemAddress;
ConnectionGraphGroupID groupId;
bool operator==( const SystemAddressAndGroupId& right ) const;
bool operator!=( const SystemAddressAndGroupId& right ) const;
bool operator > ( const SystemAddressAndGroupId& right ) const;
bool operator < ( const SystemAddressAndGroupId& right ) const;
};
// --------------------------------------------------------------------------------------------
// User functions
// --------------------------------------------------------------------------------------------
/// Plaintext encoding of the password, or 0 for none. If you use a password, use secure connections
void SetPassword(const char *password);
/// Returns the connection graph
/// \return The connection graph, stored as map of adjacency lists
DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false> *GetGraph(void);
/// Automatically add new connections to the connection graph
/// Defaults to true
/// If true, then the system will automatically add all new connections for you, assigning groupId 0 to these systems.
/// If you want more control, you should call SetAutoAddNewConnections(false);
/// When false, it is up to you to call RequestConnectionGraph and AddNewConnection to complete the graph
/// However, this way you can choose which nodes are on the graph for this system and can assign groupIds to those nodes
/// \param[in] autoAdd true to automatically add new connections to the connection graph. False to not do so.
void SetAutoAddNewConnections(bool autoAdd);
/// Requests the connection graph from another system
/// Only necessary to call if SetAutoAddNewConnections(false) is called.
/// You should call this sometime after getting ID_CONNECTION_REQUEST_ACCEPTED and \a systemAddress is or should be a node on the connection graph
/// \param[in] peer The instance of RakPeer to send through
/// \param[in] systemAddress The system to send to
void RequestConnectionGraph(RakPeerInterface *peer, SystemAddress systemAddress);
/// Adds a new connection to the connection graph from this system to the specified system. Also assigns a group identifier for that system
/// Only used and valid when SetAutoAddNewConnections(false) is called.
/// Call this for this system sometime after getting ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED for systems that contain a connection graph
/// Groups are sets of one or more nodes in the total graph
/// We only add to the graph groups which we subscribe to
/// \param[in] peer The instance of RakPeer to send through
/// \param[in] systemAddress The system that is connected to us.
/// \param[in] groupId Just a number representing a group. Important: 0 is reserved to mean unassigned group ID and is assigned to all systems added with SetAutoAddNewConnections(true)
void AddNewConnection(RakPeerInterface *peer, SystemAddress systemAddress, ConnectionGraphGroupID groupId);
/// Sets our own group ID
/// Only used and valid when SetAutoAddNewConnections(false) is called.
/// Defaults to 0
/// \param[in] groupId Our group ID
void SetGroupId(ConnectionGraphGroupID groupId);
/// Allows adding to the connection graph nodes with this groupId.
/// By default, you subscribe to group 0, which are all systems automatically added with SetAutoAddNewConnections(true)
/// Calling this does not add nodes which were previously rejected due to an unsubscribed group - it only allows addition of nodes after the fact
/// \param[in] groupId Just a number representing a group. 0 is reserved to mean unassigned group ID, automatically added with SetAutoAddNewConnections(true)
void SubscribeToGroup(ConnectionGraphGroupID groupId);
/// Disables addition of graph nodes with this groupId
/// Calling this does not add remove nodes with this groupId which are already present in the graph. It only disables addition of nodes after the fact
/// \param[in] groupId Just a number representing a group. 0 is reserved to mean unassigned group ID, automatically added with SetAutoAddNewConnections(true)
void UnsubscribeFromGroup(ConnectionGraphGroupID groupId);
// --------------------------------------------------------------------------------------------
// Packet handling functions
// --------------------------------------------------------------------------------------------
/// \internal
virtual void OnShutdown(RakPeerInterface *peer);
/// \internal
virtual void Update(RakPeerInterface *peer);
/// \internal
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal
virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress);
protected:
void HandleDroppedConnection(RakPeerInterface *peer, SystemAddress systemAddress, unsigned char packetId);
void DeleteFromPeerList(SystemAddress systemAddress);
void OnNewIncomingConnection(RakPeerInterface *peer, Packet *packet);
void OnConnectionRequestAccepted(RakPeerInterface *peer, Packet *packet);
void OnConnectionGraphRequest(RakPeerInterface *peer, Packet *packet);
void OnConnectionGraphReply(RakPeerInterface *peer, Packet *packet);
void OnConnectionGraphUpdate(RakPeerInterface *peer, Packet *packet);
void OnNewConnection(RakPeerInterface *peer, Packet *packet);
bool OnConnectionLost(RakPeerInterface *peer, Packet *packet, unsigned char packetId);
void OnConnectionAddition(RakPeerInterface *peer, Packet *packet);
void OnConnectionRemoval(RakPeerInterface *peer, Packet *packet);
void SendConnectionGraph(SystemAddress target, unsigned char packetId, RakPeerInterface *peer);
void SerializeWeightedGraph(RakNet::BitStream *out, const DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false> &g) const;
bool DeserializeWeightedGraph(RakNet::BitStream *inBitstream, RakPeerInterface *peer);
void AddAndRelayConnection(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2, unsigned short ping, RakPeerInterface *peer);
bool RemoveAndRelayConnection(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, unsigned char packetId, const SystemAddress node1, const SystemAddress node2, RakPeerInterface *peer);
void RemoveParticipant(SystemAddress systemAddress);
void AddParticipant(SystemAddress systemAddress);
void BroadcastGraphUpdate(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakPeerInterface *peer);
void NotifyUserOfRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,unsigned short ping, RakPeerInterface *peer);
bool IsNewRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,RakPeerInterface *peer);
bool DeserializeIgnoreList(DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakNet::BitStream *inBitstream );
void SerializeIgnoreListAndBroadcast(RakNet::BitStream *outBitstream, DataStructures::OrderedList<SystemAddress,SystemAddress> &ignoreList, RakPeerInterface *peer);
RakNetTime nextWeightUpdate;
char *pw;
DataStructures::OrderedList<SystemAddress, SystemAddress> participantList;
DataStructures::WeightedGraph<ConnectionGraph::SystemAddressAndGroupId, unsigned short, false> graph;
bool autoAddNewConnections;
ConnectionGraphGroupID myGroupId;
DataStructures::OrderedList<ConnectionGraphGroupID, ConnectionGraphGroupID> subscribedGroups;
// Used to broadcast new connections after some time so the pings are correct
//RakNetTime forceBroadcastTime;
};
#endif

View File

@@ -0,0 +1,277 @@
#include "ConsoleServer.h"
#include "TransportInterface.h"
#include "CommandParserInterface.h"
#include <string.h>
#include <stdlib.h>
#define COMMAND_DELINATOR ' '
#define COMMAND_DELINATOR_TOGGLE '"'
#include "LinuxStrings.h"
ConsoleServer::ConsoleServer()
{
transport=0;
password[0]=0;
}
ConsoleServer::~ConsoleServer()
{
}
void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port)
{
// Replace the current TransportInterface, stopping the old one, if present, and starting the new one.
if (transportInterface)
{
if (transport)
{
RemoveCommandParser(transport->GetCommandParser());
transport->Stop();
}
transport=transportInterface;
transport->Start(port, true);
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
commandParserList[i]->OnTransportChange(transport);
// The transport itself might have a command parser - for example password for the RakNet transport
AddCommandParser(transport->GetCommandParser());
}
}
void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface)
{
if (commandParserInterface==0)
return;
// Non-duplicate insertion
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]==commandParserInterface)
return;
if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0)
{
// Naming conflict between two command parsers
assert(0);
return;
}
}
commandParserList.Insert(commandParserInterface);
if (transport)
commandParserInterface->OnTransportChange(transport);
}
void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface)
{
if (commandParserInterface==0)
return;
// Overwrite the element we are removing from the back of the list and delete the back of the list
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]==commandParserInterface)
{
commandParserList[i]=commandParserList[commandParserList.Size()-1];
commandParserList.RemoveFromEnd();
return;
}
}
}
void ConsoleServer::Update(void)
{
unsigned i;
char *parameterList[20]; // Up to 20 parameters
unsigned numParameters;
SystemAddress newOrLostConnectionId;
Packet *p;
RegisteredCommand rc;
p = transport->Receive();
newOrLostConnectionId=transport->HasNewConnection();
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
{
for (i=0; i < commandParserList.Size(); i++)
{
commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport);
}
transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n");
ListParsers(newOrLostConnectionId);
}
newOrLostConnectionId=transport->HasLostConnection();
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
{
for (i=0; i < commandParserList.Size(); i++)
commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport);
}
while (p)
{
bool commandParsed=false;
char copy[REMOTE_MAX_TEXT_INPUT];
memcpy(copy, p->data, p->length);
copy[p->length]=0;
CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters
if (numParameters==0)
{
transport->DeallocatePacket(p);
p = transport->Receive();
continue;
}
if (_stricmp(*parameterList, "help")==0 && numParameters<=2)
{
// Find the parser specified and display help for it
if (numParameters==1)
{
transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n");
transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n");
transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n");
transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n");
transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n");
transport->Send(p->systemAddress, "COMMANDS:\r\n");
transport->Send(p->systemAddress, "help Show this display.\r\n");
transport->Send(p->systemAddress, "help <ParserName> Show help on a particular parser.\r\n");
transport->Send(p->systemAddress, "help <CommandName> Show help on a particular command.\r\n");
transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n");
transport->Send(p->systemAddress, "[<ParserName>] <Command> [<Parameters>] Execute a command\r\n");
transport->Send(p->systemAddress, "[<ParserNumber>] <Command> [<Parameters>] Execute a command\r\n");
ListParsers(p->systemAddress);
}
else // numParameters == 2, including the help tag
{
for (i=0; i < commandParserList.Size(); i++)
{
if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0)
{
commandParsed=true;
commandParserList[i]->SendHelp(transport, p->systemAddress);
transport->Send(p->systemAddress, "COMMAND LIST:\r\n");
commandParserList[i]->SendCommandList(transport, p->systemAddress);
transport->Send(p->systemAddress, "\r\n");
break;
}
}
if (commandParsed==false)
{
// Try again, for all commands for all parsers.
RegisteredCommand rc;
for (i=0; i < commandParserList.Size(); i++)
{
if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc))
{
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS)
transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp);
else
transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
commandParsed=true;
break;
}
}
}
if (commandParsed==false)
{
// Don't know what to do
transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]);
}
}
}
else if (_stricmp(*parameterList, "quit")==0 && numParameters==1)
{
transport->Send(p->systemAddress, "Goodbye!");
transport->CloseConnection(p->systemAddress);
}
else
{
bool tryAllParsers=true;
bool failed=false;
if (numParameters >=2) // At minimum <CommandParserName> <Command>
{
unsigned commandParserIndex=(unsigned)-1;
// Prefixing with numbers directs to a particular parser
if (**parameterList>='0' && **parameterList<='9')
{
commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number
commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1
if (commandParserIndex >= commandParserList.Size())
{
transport->Send(p->systemAddress, "Invalid index.\r\n");
failed=true;
}
}
else
{
// // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser
for (i=0; i < commandParserList.Size(); i++)
{
if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0)
{
commandParserIndex=i; // Matches parser at index i
break;
}
}
}
if (failed==false)
{
// -1 means undirected, so otherwise this is directed to a target
if (commandParserIndex!=(unsigned)-1)
{
// Only this parser should use this command
tryAllParsers=false;
if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc))
{
commandParsed=true;
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2)
commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy);
else
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
}
}
}
}
if (failed == false && tryAllParsers)
{
for (i=0; i < commandParserList.Size(); i++)
{
// Undirected command. Try all the parsers to see if they understand the command
// Pass the 1nd element as the command, and the remainder as the parameter list
if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc))
{
commandParsed=true;
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1)
commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy);
else
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
}
}
}
if (commandParsed==false && commandParserList.Size() > 0)
{
transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n");
}
}
transport->DeallocatePacket(p);
p = transport->Receive();
}
}
void ConsoleServer::ListParsers(SystemAddress systemAddress)
{
transport->Send(systemAddress,"INSTALLED PARSERS:\r\n");
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName());
}
}

View File

@@ -0,0 +1,63 @@
/// \file
/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __CONSOLE_SERVER_H
#define __CONSOLE_SERVER_H
class TransportInterface;
class CommandParserInterface;
#include "RakMemoryOverride.h"
#include "DS_List.h"
#include "RakNetTypes.h"
#include "Export.h"
/// \brief The main entry point for the server portion of your remote console application support.
/// ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s)
/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the
/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations .
/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface
class RAK_DLL_EXPORT ConsoleServer : public RakNet::RakMemoryOverride
{
public:
ConsoleServer();
~ConsoleServer();
/// Call this with a derivation of TransportInterface so that the console server can send and receive commands
/// \param[in] transportInterface Your interface to use.
/// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want.
void SetTransportProvider(TransportInterface *transportInterface, unsigned short port);
/// Add an implementation of CommandParserInterface to the list of command parsers.
/// \param[in] commandParserInterface The command parser referred to
void AddCommandParser(CommandParserInterface *commandParserInterface);
/// Remove an implementation of CommandParserInterface previously added with AddCommandParser()
/// \param[in] commandParserInterface The command parser referred to
void RemoveCommandParser(CommandParserInterface *commandParserInterface);
/// Call update to read packet sent from your TransportInterface.
/// You should do this fairly frequently.
void Update(void);
protected:
void ListParsers(SystemAddress systemAddress);
TransportInterface *transport;
DataStructures::List<CommandParserInterface *> commandParserList;
char* password[256];
};
#endif

1162
thirdparty/raknet/Source/DS_BPlusTree.h vendored Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

146
thirdparty/raknet/Source/DS_BytePool.cpp vendored Normal file
View File

@@ -0,0 +1,146 @@
#include "DS_BytePool.h"
#include <assert.h>
#ifndef __APPLE__
// Use stdlib and not malloc for compatibility
#include <stdlib.h>
#endif
using namespace DataStructures;
BytePool::BytePool()
{
pool128.SetPageSize(8192*4);
pool512.SetPageSize(8192*4);
pool2048.SetPageSize(8192*4);
pool8192.SetPageSize(8192*4);
}
BytePool::~BytePool()
{
}
void BytePool::SetPageSize(int size)
{
pool128.SetPageSize(size);
pool512.SetPageSize(size);
pool2048.SetPageSize(size);
pool8192.SetPageSize(size);
}
unsigned char *BytePool::Allocate(int bytesWanted)
{
#ifdef _DISABLE_BYTE_POOL
return rakMalloc(bytesWanted);
#endif
unsigned char *out;
if (bytesWanted <= 127)
{
#ifdef _THREADSAFE_BYTE_POOL
mutex128.Lock();
#endif
out = (unsigned char*) pool128.Allocate();
#ifdef _THREADSAFE_BYTE_POOL
mutex128.Unlock();
#endif
out[0]=0;
return ((unsigned char*) out)+1;
}
if (bytesWanted <= 511)
{
#ifdef _THREADSAFE_BYTE_POOL
mutex512.Lock();
#endif
out = (unsigned char*) pool512.Allocate();
#ifdef _THREADSAFE_BYTE_POOL
mutex512.Unlock();
#endif
out[0]=1;
return ((unsigned char*) out)+1;
}
if (bytesWanted <= 2047)
{
#ifdef _THREADSAFE_BYTE_POOL
mutex2048.Lock();
#endif
out = (unsigned char*) pool2048.Allocate();
#ifdef _THREADSAFE_BYTE_POOL
mutex2048.Unlock();
#endif
out[0]=2;
return ((unsigned char*) out)+1;
}
if (bytesWanted <= 8191)
{
#ifdef _THREADSAFE_BYTE_POOL
mutex8192.Lock();
#endif
out = (unsigned char*) pool8192.Allocate();
#ifdef _THREADSAFE_BYTE_POOL
mutex8192.Unlock();
#endif
out[0]=3;
return ((unsigned char*) out)+1;
}
out = (unsigned char*) rakMalloc(bytesWanted+1);
out[0]=(unsigned char)255;
return out+1;
}
void BytePool::Release(unsigned char *data)
{
#ifdef _DISABLE_BYTE_POOL
RakFree(data);
#endif
unsigned char *realData = data-1;
switch (realData[0])
{
case 0:
#ifdef _THREADSAFE_BYTE_POOL
mutex128.Lock();
#endif
pool128.Release((unsigned char(*)[128]) realData );
#ifdef _THREADSAFE_BYTE_POOL
mutex128.Unlock();
#endif
break;
case 1:
#ifdef _THREADSAFE_BYTE_POOL
mutex512.Lock();
#endif
pool512.Release((unsigned char(*)[512]) realData );
#ifdef _THREADSAFE_BYTE_POOL
mutex512.Unlock();
#endif
break;
case 2:
#ifdef _THREADSAFE_BYTE_POOL
mutex2048.Lock();
#endif
pool2048.Release((unsigned char(*)[2048]) realData );
#ifdef _THREADSAFE_BYTE_POOL
mutex2048.Unlock();
#endif
break;
case 3:
#ifdef _THREADSAFE_BYTE_POOL
mutex8192.Lock();
#endif
pool8192.Release((unsigned char(*)[8192]) realData );
#ifdef _THREADSAFE_BYTE_POOL
mutex8192.Unlock();
#endif
break;
case 255:
rakFree(realData);
break;
default:
assert(0);
break;
}
}
void BytePool::Clear(void)
{
#ifdef _THREADSAFE_BYTE_POOL
pool128.Clear();
pool512.Clear();
pool2048.Clear();
pool8192.Clear();
#endif
}

40
thirdparty/raknet/Source/DS_BytePool.h vendored Normal file
View File

@@ -0,0 +1,40 @@
#ifndef __BYTE_POOL_H
#define __BYTE_POOL_H
#include "RakMemoryOverride.h"
#include "DS_MemoryPool.h"
#include "Export.h"
#include "SimpleMutex.h"
#include <assert.h>
// #define _DISABLE_BYTE_POOL
// #define _THREADSAFE_BYTE_POOL
namespace DataStructures
{
// Allocate some number of bytes from pools. Uses the heap if necessary.
class RAK_DLL_EXPORT BytePool : public RakNet::RakMemoryOverride
{
public:
BytePool();
~BytePool();
// Should be at least 8 times bigger than 8192
void SetPageSize(int size);
unsigned char* Allocate(int bytesWanted);
void Release(unsigned char *data);
void Clear(void);
protected:
MemoryPool<unsigned char[128]> pool128;
MemoryPool<unsigned char[512]> pool512;
MemoryPool<unsigned char[2048]> pool2048;
MemoryPool<unsigned char[8192]> pool8192;
#ifdef _THREADSAFE_BYTE_POOL
SimpleMutex mutex128;
SimpleMutex mutex512;
SimpleMutex mutex2048;
SimpleMutex mutex8192;
#endif
};
}
#endif

View File

@@ -0,0 +1,101 @@
#include "DS_ByteQueue.h"
#include <string.h> // Memmove
#include <stdlib.h> // realloc
#include <stdio.h>
using namespace DataStructures;
ByteQueue::ByteQueue()
{
readOffset=writeOffset=lengthAllocated=0;
data=0;
}
ByteQueue::~ByteQueue()
{
Clear();
}
void ByteQueue::WriteBytes(const char *in, unsigned length)
{
unsigned bytesWritten;
bytesWritten=GetBytesWritten();
if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1)
{
unsigned oldLengthAllocated=lengthAllocated;
unsigned newAmountToAllocate=length*2;
if (newAmountToAllocate<256)
newAmountToAllocate=256;
lengthAllocated=lengthAllocated + newAmountToAllocate;
data=(char*)rakRealloc(data, lengthAllocated);
if (writeOffset < readOffset)
{
if (writeOffset <= newAmountToAllocate)
{
memcpy(data + oldLengthAllocated, data, writeOffset);
writeOffset=readOffset+bytesWritten;
}
else
{
memcpy(data + oldLengthAllocated, data, newAmountToAllocate);
memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate);
writeOffset-=newAmountToAllocate;
}
}
}
if (length <= lengthAllocated-writeOffset)
memcpy(data+writeOffset, in, length);
else
{
// Wrap
memcpy(data+writeOffset, in, lengthAllocated-writeOffset);
memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset));
}
writeOffset=(writeOffset+length) % lengthAllocated;
}
bool ByteQueue::ReadBytes(char *out, unsigned length, bool peek)
{
if (GetBytesWritten() < length)
return false;
if (length <= lengthAllocated-readOffset)
memcpy(out, data+readOffset, length);
else
{
// Wrap
memcpy(out, data+readOffset, lengthAllocated-readOffset);
memcpy(out+(lengthAllocated-readOffset), data, length-(lengthAllocated-readOffset));
}
if (peek==false)
IncrementReadOffset(length);
return true;
}
void ByteQueue::Clear(void)
{
if (lengthAllocated)
rakFree(data);
readOffset=writeOffset=lengthAllocated=0;
data=0;
}
unsigned ByteQueue::GetBytesWritten(void) const
{
if (writeOffset>=readOffset)
return writeOffset-readOffset;
else
return (writeOffset-1)+(lengthAllocated-readOffset);
}
void ByteQueue::IncrementReadOffset(unsigned length)
{
readOffset=(readOffset+length) % lengthAllocated;
}
void ByteQueue::Print(void)
{
unsigned i;
for (i=readOffset; i!=writeOffset; i++)
printf("%i ", data[i]);
printf("\n");
}

46
thirdparty/raknet/Source/DS_ByteQueue.h vendored Normal file
View File

@@ -0,0 +1,46 @@
/// \file
/// \brief \b [Internal] Byte queue
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __BYTE_QUEUE_H
#define __BYTE_QUEUE_H
#include "RakMemoryOverride.h"
#include "Export.h"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
class ByteQueue : public RakNet::RakMemoryOverride
{
public:
ByteQueue();
~ByteQueue();
void WriteBytes(const char *in, unsigned length);
bool ReadBytes(char *out, unsigned length, bool peek);
unsigned GetBytesWritten(void) const;
void IncrementReadOffset(unsigned length);
void Clear(void);
void Print(void);
protected:
char *data;
unsigned readOffset, writeOffset, lengthAllocated;
};
}
#endif

251
thirdparty/raknet/Source/DS_Heap.h vendored Normal file
View File

@@ -0,0 +1,251 @@
/// \file
/// \brief \b [Internal] Heap (Also serves as a priority queue)
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __RAKNET_HEAP_H
#define __RAKNET_HEAP_H
#include "RakMemoryOverride.h"
#include "DS_List.h"
#include "Export.h"
#include <assert.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
template <class weight_type, class data_type, bool isMaxHeap>
class RAK_DLL_EXPORT Heap : public RakNet::RakMemoryOverride
{
public:
struct HeapNode
{
HeapNode() {}
HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {}
weight_type weight; // I'm assuming key is a native numerical type - float or int
data_type data;
};
Heap();
~Heap();
void Push(const weight_type &weight, const data_type &data);
data_type Pop(const unsigned startingIndex);
data_type Peek(const unsigned startingIndex=0) const;
weight_type PeekWeight(const unsigned startingIndex=0) const;
void Clear(void);
data_type& operator[] ( const unsigned int position ) const;
unsigned Size(void) const;
protected:
unsigned LeftChild(const unsigned i) const;
unsigned RightChild(const unsigned i) const;
unsigned Parent(const unsigned i) const;
void Swap(const unsigned i, const unsigned j);
DataStructures::List<HeapNode> heap;
};
template <class weight_type, class data_type, bool isMaxHeap>
Heap<weight_type, data_type, isMaxHeap>::Heap()
{
}
template <class weight_type, class data_type, bool isMaxHeap>
Heap<weight_type, data_type, isMaxHeap>::~Heap()
{
Clear();
}
template <class weight_type, class data_type, bool isMaxHeap>
void Heap<weight_type, data_type, isMaxHeap>::Push(const weight_type &weight, const data_type &data)
{
unsigned currentIndex = heap.Size();
unsigned parentIndex;
heap.Insert(HeapNode(weight, data));
while (currentIndex!=0)
{
parentIndex = Parent(currentIndex);
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
if (isMaxHeap)
{
if (heap[parentIndex].weight < weight)
{
Swap(currentIndex, parentIndex);
currentIndex=parentIndex;
}
else
break;
}
else
{
if (heap[parentIndex].weight > weight)
{
Swap(currentIndex, parentIndex);
currentIndex=parentIndex;
}
else
break;
}
}
}
template <class weight_type, class data_type, bool isMaxHeap>
data_type Heap<weight_type, data_type, isMaxHeap>::Pop(const unsigned startingIndex)
{
// While we have children, swap out with the larger of the two children.
// This line will assert on an empty heap
data_type returnValue=heap[0].data;
// Move the last element to the head, and re-heapify
heap[startingIndex]=heap[heap.Size()-1];
unsigned currentIndex,leftChild,rightChild;
weight_type currentWeight;
currentIndex=startingIndex;
currentWeight=heap[startingIndex].weight;
heap.RemoveFromEnd();
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while (1)
{
leftChild=LeftChild(currentIndex);
rightChild=RightChild(currentIndex);
if (leftChild >= heap.Size())
{
// Done
return returnValue;
}
if (rightChild >= heap.Size())
{
// Only left node.
if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) ||
(isMaxHeap==false && currentWeight > heap[leftChild].weight))
Swap(leftChild, currentIndex);
return returnValue;
}
else
{
// Swap with the bigger/smaller of the two children and continue
if (isMaxHeap)
{
if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight)
return returnValue;
if (heap[leftChild].weight > heap[rightChild].weight)
{
Swap(leftChild, currentIndex);
currentIndex=leftChild;
}
else
{
Swap(rightChild, currentIndex);
currentIndex=rightChild;
}
}
else
{
if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight)
return returnValue;
if (heap[leftChild].weight < heap[rightChild].weight)
{
Swap(leftChild, currentIndex);
currentIndex=leftChild;
}
else
{
Swap(rightChild, currentIndex);
currentIndex=rightChild;
}
}
}
}
}
template <class weight_type, class data_type, bool isMaxHeap>
data_type Heap<weight_type, data_type, isMaxHeap>::Peek(const unsigned startingIndex) const
{
return heap[startingIndex].data;
}
template <class weight_type, class data_type, bool isMaxHeap>
weight_type Heap<weight_type, data_type, isMaxHeap>::PeekWeight(const unsigned startingIndex) const
{
return heap[startingIndex].weight;
}
template <class weight_type, class data_type, bool isMaxHeap>
void Heap<weight_type, data_type, isMaxHeap>::Clear(void)
{
heap.Clear();
}
template <class weight_type, class data_type, bool isMaxHeap>
data_type& Heap<weight_type, data_type, isMaxHeap>::operator[] ( const unsigned int position ) const
{
return heap[position].data;
}
template <class weight_type, class data_type, bool isMaxHeap>
unsigned Heap<weight_type, data_type, isMaxHeap>::Size(void) const
{
return heap.Size();
}
template <class weight_type, class data_type, bool isMaxHeap>
unsigned Heap<weight_type, data_type, isMaxHeap>::LeftChild(const unsigned i) const
{
return i*2+1;
}
template <class weight_type, class data_type, bool isMaxHeap>
unsigned Heap<weight_type, data_type, isMaxHeap>::RightChild(const unsigned i) const
{
return i*2+2;
}
template <class weight_type, class data_type, bool isMaxHeap>
unsigned Heap<weight_type, data_type, isMaxHeap>::Parent(const unsigned i) const
{
#ifdef _DEBUG
assert(i!=0);
#endif
return (i-1)/2;
}
template <class weight_type, class data_type, bool isMaxHeap>
void Heap<weight_type, data_type, isMaxHeap>::Swap(const unsigned i, const unsigned j)
{
HeapNode temp;
temp=heap[i];
heap[i]=heap[j];
heap[j]=temp;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

View File

@@ -0,0 +1,306 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "DS_HuffmanEncodingTree.h"
#include "DS_Queue.h"
#include "BitStream.h"
#include <assert.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
HuffmanEncodingTree::HuffmanEncodingTree()
{
root = 0;
}
HuffmanEncodingTree::~HuffmanEncodingTree()
{
FreeMemory();
}
void HuffmanEncodingTree::FreeMemory( void )
{
if ( root == 0 )
return ;
// Use an in-order traversal to delete the tree
DataStructures::Queue<HuffmanEncodingTreeNode *> nodeQueue;
HuffmanEncodingTreeNode *node;
nodeQueue.Push( root );
while ( nodeQueue.Size() > 0 )
{
node = nodeQueue.Pop();
if ( node->left )
nodeQueue.Push( node->left );
if ( node->right )
nodeQueue.Push( node->right );
delete node;
}
// Delete the encoding table
for ( int i = 0; i < 256; i++ )
rakFree(encodingTable[ i ].encoding);
root = 0;
}
////#include <stdio.h>
// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree
void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] )
{
int counter;
HuffmanEncodingTreeNode * node;
HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier
// 1. Make 256 trees each with a weight equal to the frequency of the corresponding character
DataStructures::LinkedList<HuffmanEncodingTreeNode *> huffmanEncodingTreeNodeList;
FreeMemory();
for ( counter = 0; counter < 256; counter++ )
{
node = new HuffmanEncodingTreeNode;
node->left = 0;
node->right = 0;
node->value = (unsigned char) counter;
node->weight = frequencyTable[ counter ];
if ( node->weight == 0 )
node->weight = 1; // 0 weights are illegal
leafList[ counter ] = node; // Used later to generate the encryption table
InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order.
}
// 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right
// children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes.
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while ( 1 )
{
huffmanEncodingTreeNodeList.Beginning();
HuffmanEncodingTreeNode *lesser, *greater;
lesser = huffmanEncodingTreeNodeList.Pop();
greater = huffmanEncodingTreeNodeList.Pop();
node = new HuffmanEncodingTreeNode;
node->left = lesser;
node->right = greater;
node->weight = lesser->weight + greater->weight;
lesser->parent = node; // This is done to make generating the encryption table easier
greater->parent = node; // This is done to make generating the encryption table easier
if ( huffmanEncodingTreeNodeList.Size() == 0 )
{
// 3. Assign the one remaining node in the list to the root node.
root = node;
root->parent = 0;
break;
}
// Put the new node back into the list at the correct spot to maintain the sort. Linear search time
InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList );
}
bool tempPath[ 256 ]; // Maximum path length is 256
unsigned short tempPathLength;
HuffmanEncodingTreeNode *currentNode;
RakNet::BitStream bitStream;
// Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents.
// This can be done more efficiently but this isn't bad and it's way easier to program and debug
for ( counter = 0; counter < 256; counter++ )
{
// Already done at the end of the loop and before it!
tempPathLength = 0;
// Set the current node at the leaf
currentNode = leafList[ counter ];
do
{
if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root
tempPath[ tempPathLength++ ] = false;
else
tempPath[ tempPathLength++ ] = true;
currentNode = currentNode->parent;
}
while ( currentNode != root );
// Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf
while ( tempPathLength-- > 0 )
{
if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want
bitStream.Write1();
else
bitStream.Write0();
}
// Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer
encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding );
// Reset the bitstream for the next iteration
bitStream.Reset();
}
}
// Pass an array of bytes to array and a preallocated BitStream to receive the output
void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output )
{
unsigned counter;
// For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation
for ( counter = 0; counter < sizeInBytes; counter++ )
{
output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned
}
// Byte align the output so the unassigned remaining bits don't equate to some actual value
if ( output->GetNumberOfBitsUsed() % 8 != 0 )
{
// Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned.
unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) );
for ( counter = 0; counter < 256; counter++ )
if ( encodingTable[ counter ].bitLength > remainingBits )
{
output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned
break;
}
#ifdef _DEBUG
assert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits
#endif
}
}
unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output )
{
HuffmanEncodingTreeNode * currentNode;
unsigned outputWriteIndex;
outputWriteIndex = 0;
currentNode = root;
// For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root
for ( unsigned counter = 0; counter < sizeInBits; counter++ )
{
if ( input->ReadBit() == false ) // left!
currentNode = currentNode->left;
else
currentNode = currentNode->right;
if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf
{
if ( outputWriteIndex < maxCharsToWrite )
output[ outputWriteIndex ] = currentNode->value;
outputWriteIndex++;
currentNode = root;
}
}
return outputWriteIndex;
}
// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output
void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output )
{
HuffmanEncodingTreeNode * currentNode;
if ( sizeInBits <= 0 )
return ;
RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false );
currentNode = root;
// For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root
for ( unsigned counter = 0; counter < sizeInBits; counter++ )
{
if ( bitStream.ReadBit() == false ) // left!
currentNode = currentNode->left;
else
currentNode = currentNode->right;
if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf
{
output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING
currentNode = root;
}
}
}
// Insertion sort. Slow but easy to write in this case
void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList<HuffmanEncodingTreeNode *> *huffmanEncodingTreeNodeList ) const
{
if ( huffmanEncodingTreeNodeList->Size() == 0 )
{
huffmanEncodingTreeNodeList->Insert( node );
return ;
}
huffmanEncodingTreeNodeList->Beginning();
unsigned counter = 0;
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while ( 1 )
{
if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight )
++( *huffmanEncodingTreeNodeList );
else
{
huffmanEncodingTreeNodeList->Insert( node );
break;
}
// Didn't find a spot in the middle - add to the end
if ( ++counter == huffmanEncodingTreeNodeList->Size() )
{
huffmanEncodingTreeNodeList->End();
huffmanEncodingTreeNodeList->Add( node )
; // Add to the end
break;
}
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,71 @@
/// \file
/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __HUFFMAN_ENCODING_TREE
#define __HUFFMAN_ENCODING_TREE
#include "RakMemoryOverride.h"
#include "DS_HuffmanEncodingTreeNode.h"
#include "BitStream.h"
#include "Export.h"
#include "DS_LinkedList.h"
/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1
class RAK_DLL_EXPORT HuffmanEncodingTree : public RakNet::RakMemoryOverride
{
public:
HuffmanEncodingTree();
~HuffmanEncodingTree();
/// Pass an array of bytes to array and a preallocated BitStream to receive the output
/// \param [in] input Array of bytes to encode
/// \param [in] sizeInBytes size of \a input
/// \param [out] output The bitstream to write to
void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output );
// Decodes an array encoded by EncodeArray()
unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output );
void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output );
/// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree
void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] );
/// Free the memory used by the tree
void FreeMemory( void );
private:
/// The root node of the tree
HuffmanEncodingTreeNode *root;
/// Used to hold bit encoding for one character
struct CharacterEncoding
{
unsigned char* encoding;
unsigned short bitLength;
};
CharacterEncoding encodingTable[ 256 ];
void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList<HuffmanEncodingTreeNode *> *huffmanEncodingTreeNodeList ) const;
};
#endif

View File

@@ -0,0 +1,61 @@
/// \file
/// \brief \b [Internal] Creates instances of the class HuffmanEncodingTree
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __HUFFMAN_ENCODING_TREE_FACTORY
#define __HUFFMAN_ENCODING_TREE_FACTORY
#include "RakMemoryOverride.h"
class HuffmanEncodingTree;
/// \brief Creates instances of the class HuffmanEncodingTree
///
/// This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree
class HuffmanEncodingTreeFactory : public RakNet::RakMemoryOverride
{
public:
/// Default constructor
HuffmanEncodingTreeFactory();
/// Reset the frequency table. You don't need to call this unless you want to reuse the class for a new tree
void Reset( void );
/// Pass an array of bytes to this to add those elements to the frequency table
/// \param[in] array the data to insert into the frequency table
/// \param[in] size the size of the data to insert
void AddToFrequencyTable( unsigned char *array, int size );
/// Copies the frequency table to the array passed
/// Retrieve the frequency table
/// \param[in] _frequency The frequency table used currently
void GetFrequencyTable( unsigned int _frequency[ 256 ] );
/// Returns the frequency table as a pointer
/// \return the address of the frenquency table
unsigned int * GetFrequencyTable( void );
/// Generate a HuffmanEncodingTree.
/// You can also use GetFrequencyTable and GenerateFromFrequencyTable in the tree itself
/// \return The generated instance of HuffmanEncodingTree
HuffmanEncodingTree * GenerateTree( void );
private:
/// Frequency table
unsigned int frequency[ 256 ];
};
#endif

View File

@@ -0,0 +1,30 @@
/// \file
/// \brief \b [Internal] A single node in the Huffman Encoding Tree.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __HUFFMAN_ENCODING_TREE_NODE
#define __HUFFMAN_ENCODING_TREE_NODE
struct HuffmanEncodingTreeNode
{
unsigned char value;
unsigned weight;
HuffmanEncodingTreeNode *left;
HuffmanEncodingTreeNode *right;
HuffmanEncodingTreeNode *parent;
};
#endif

1259
thirdparty/raknet/Source/DS_LinkedList.h vendored Normal file

File diff suppressed because it is too large Load Diff

512
thirdparty/raknet/Source/DS_List.h vendored Normal file
View File

@@ -0,0 +1,512 @@
/// \file
/// \brief \b [Internal] Array based list. Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __LIST_H
#define __LIST_H
#include <assert.h>
#include <string.h> // memmove
#include "Export.h"
/// Maximum unsigned long
static const unsigned int MAX_UNSIGNED_LONG = 4294967295U;
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
/// \brief Array based implementation of a list.
/// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance.
template <class list_type>
class RAK_DLL_EXPORT List
{
public:
/// Default constructor
List();
/// Destructor
~List();
/// Copy constructor
/// \param[in] original_copy The list to duplicate
List( const List& original_copy );
/// Assign one list to another
List& operator= ( const List& original_copy );
/// Access an element by its index in the array
/// \param[in] position The index into the array.
/// \return The element at position \a position.
list_type& operator[] ( const unsigned int position ) const;
/// Push an element at the end of the stack
/// \param[in] input The new element.
void Push(const list_type input);
/// Pop an element from the end of the stack
/// \pre Size()>0
/// \return The element at the end.
list_type& Pop(void);
/// Insert an element at position \a position in the list
/// \param[in] input The new element.
/// \param[in] position The position of the new element.
void Insert( const list_type input, const unsigned int position );
/// Insert at the end of the list.
/// \param[in] input The new element.
void Insert( const list_type input );
/// Replace the value at \a position by \a input. If the size of
/// the list is less than @em position, it increase the capacity of
/// the list and fill slot with @em filler.
/// \param[in] input The element to replace at position @em position.
/// \param[in] filler The element use to fill new allocated capacity.
/// \param[in] position The position of input in the list.
void Replace( const list_type input, const list_type filler, const unsigned int position );
/// Replace the last element of the list by \a input .
/// \param[in] input The element used to replace the last element.
void Replace( const list_type input );
/// Delete the element at position \a position.
/// \param[in] position The index of the element to delete
void RemoveAtIndex( const unsigned int position );
/// Delete the element at position \a position.
/// \note - swaps middle with end of list, only use if list order does not matter
/// \param[in] position The index of the element to delete
void RemoveAtIndexFast( const unsigned int position );
/// Delete the element at the end of the list
void RemoveFromEnd(const unsigned num=1);
/// Returns the index of the specified item or MAX_UNSIGNED_LONG if not found
/// \param[in] input The element to check for
/// \return The index or position of @em input in the list.
/// \retval MAX_UNSIGNED_LONG The object is not in the list
/// \retval [Integer] The index of the element in the list
unsigned int GetIndexOf( const list_type input ) const;
/// \return The number of elements in the list
unsigned int Size( void ) const;
/// Clear the list
void Clear( bool doNotDeallocateSmallBlocks=false );
// Preallocate the list, so it needs fewer reallocations at runtime
void Preallocate( unsigned countNeeded );
/// Frees overallocated members, to use the minimum memory necessary
/// \attention
/// This is a slow operation
void Compress( void );
private:
/// An array of user values
list_type* listArray;
/// Number of elements in the list
unsigned int list_size;
/// Size of \a array
unsigned int allocation_size;
};
template <class list_type>
List<list_type>::List()
{
allocation_size = 0;
listArray = 0;
list_size = 0;
}
template <class list_type>
List<list_type>::~List()
{
if (allocation_size>0)
delete [] listArray;
}
template <class list_type>
List<list_type>::List( const List& original_copy )
{
// Allocate memory for copy
if ( original_copy.list_size == 0 )
{
list_size = 0;
allocation_size = 0;
}
else
{
listArray = new list_type [ original_copy.list_size ];
for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter )
listArray[ counter ] = original_copy.listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type));
list_size = allocation_size = original_copy.list_size;
}
}
template <class list_type>
List<list_type>& List<list_type>::operator= ( const List& original_copy )
{
if ( ( &original_copy ) != this )
{
Clear();
// Allocate memory for copy
if ( original_copy.list_size == 0 )
{
list_size = 0;
allocation_size = 0;
}
else
{
listArray = new list_type [ original_copy.list_size ];
for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter )
listArray[ counter ] = original_copy.listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type));
list_size = allocation_size = original_copy.list_size;
}
}
return *this;
}
template <class list_type>
inline list_type& List<list_type>::operator[] ( const unsigned int position ) const
{
#ifdef _DEBUG
if (position>=list_size)
{
assert ( position < list_size );
}
#endif
return listArray[ position ];
}
template <class list_type>
void List<list_type>::Push(const list_type input)
{
Insert(input);
}
template <class list_type>
inline list_type& List<list_type>::Pop(void)
{
#ifdef _DEBUG
assert(list_size>0);
#endif
--list_size;
return listArray[list_size];
}
template <class list_type>
void List<list_type>::Insert( const list_type input, const unsigned int position )
{
#ifdef _DEBUG
if (position>list_size)
{
assert( position <= list_size );
}
#endif
// Reallocate list if necessary
if ( list_size == allocation_size )
{
// allocate twice the currently allocated memory
list_type * new_array;
if ( allocation_size == 0 )
allocation_size = 16;
else
allocation_size *= 2;
new_array = new list_type [ allocation_size ];
// copy old array over
for ( unsigned int counter = 0; counter < list_size; ++counter )
new_array[ counter ] = listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(new_array, listArray, list_size*sizeof(list_type));
// set old array to point to the newly allocated and twice as large array
delete[] listArray;
listArray = new_array;
}
// Move the elements in the list to make room
for ( unsigned int counter = list_size; counter != position; counter-- )
listArray[ counter ] = listArray[ counter - 1 ];
// Don't call constructors, assignment operators, etc.
//memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type));
// Insert the new item at the correct spot
listArray[ position ] = input;
++list_size;
}
template <class list_type>
void List<list_type>::Insert( const list_type input )
{
// Reallocate list if necessary
if ( list_size == allocation_size )
{
// allocate twice the currently allocated memory
list_type * new_array;
if ( allocation_size == 0 )
allocation_size = 16;
else
allocation_size *= 2;
new_array = new list_type [ allocation_size ];
if (listArray)
{
// copy old array over
for ( unsigned int counter = 0; counter < list_size; ++counter )
new_array[ counter ] = listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(new_array, listArray, list_size*sizeof(list_type));
// set old array to point to the newly allocated and twice as large array
delete[] listArray;
}
listArray = new_array;
}
// Insert the new item at the correct spot
listArray[ list_size ] = input;
++list_size;
}
template <class list_type>
inline void List<list_type>::Replace( const list_type input, const list_type filler, const unsigned int position )
{
if ( ( list_size > 0 ) && ( position < list_size ) )
{
// Direct replacement
listArray[ position ] = input;
}
else
{
if ( position >= allocation_size )
{
// Reallocate the list to size position and fill in blanks with filler
list_type * new_array;
allocation_size = position + 1;
new_array = new list_type [ allocation_size ];
// copy old array over
for ( unsigned int counter = 0; counter < list_size; ++counter )
new_array[ counter ] = listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(new_array, listArray, list_size*sizeof(list_type));
// set old array to point to the newly allocated array
delete[] listArray;
listArray = new_array;
}
// Fill in holes with filler
while ( list_size < position )
listArray[ list_size++ ] = filler;
// Fill in the last element with the new item
listArray[ list_size++ ] = input;
#ifdef _DEBUG
assert( list_size == position + 1 );
#endif
}
}
template <class list_type>
inline void List<list_type>::Replace( const list_type input )
{
if ( list_size > 0 )
listArray[ list_size - 1 ] = input;
}
template <class list_type>
void List<list_type>::RemoveAtIndex( const unsigned int position )
{
#ifdef _DEBUG
if (position >= list_size)
{
assert( position < list_size );
return;
}
#endif
if ( position < list_size )
{
// Compress the array
for ( unsigned int counter = position; counter < list_size - 1 ; ++counter )
listArray[ counter ] = listArray[ counter + 1 ];
// Don't call constructors, assignment operators, etc.
// memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type));
RemoveFromEnd();
}
}
template <class list_type>
void List<list_type>::RemoveAtIndexFast( const unsigned int position )
{
#ifdef _DEBUG
if (position >= list_size)
{
assert( position < list_size );
return;
}
#endif
--list_size;
listArray[position]=listArray[list_size];
}
template <class list_type>
inline void List<list_type>::RemoveFromEnd( const unsigned num )
{
// Delete the last elements on the list. No compression needed
#ifdef _DEBUG
assert(list_size>=num);
#endif
list_size-=num;
}
template <class list_type>
unsigned int List<list_type>::GetIndexOf( const list_type input ) const
{
for ( unsigned int i = 0; i < list_size; ++i )
if ( listArray[ i ] == input )
return i;
return MAX_UNSIGNED_LONG;
}
template <class list_type>
inline unsigned int List<list_type>::Size( void ) const
{
return list_size;
}
template <class list_type>
void List<list_type>::Clear( bool doNotDeallocateSmallBlocks )
{
if ( allocation_size == 0 )
return;
if (allocation_size>512 || doNotDeallocateSmallBlocks==false)
{
delete [] listArray;
allocation_size = 0;
listArray = 0;
}
list_size = 0;
}
template <class list_type>
void List<list_type>::Compress( void )
{
list_type * new_array;
if ( allocation_size == 0 )
return ;
new_array = new list_type [ allocation_size ];
// copy old array over
for ( unsigned int counter = 0; counter < list_size; ++counter )
new_array[ counter ] = listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(new_array, listArray, list_size*sizeof(list_type));
// set old array to point to the newly allocated array
delete[] listArray;
listArray = new_array;
}
template <class list_type>
void List<list_type>::Preallocate( unsigned countNeeded )
{
unsigned amountToAllocate = allocation_size;
if (allocation_size==0)
amountToAllocate=16;
while (amountToAllocate < countNeeded)
amountToAllocate<<=1;
if ( allocation_size < amountToAllocate)
{
// allocate twice the currently allocated memory
list_type * new_array;
allocation_size=amountToAllocate;
new_array = new list_type [ allocation_size ];
if (listArray)
{
// copy old array over
for ( unsigned int counter = 0; counter < list_size; ++counter )
new_array[ counter ] = listArray[ counter ];
// Don't call constructors, assignment operators, etc.
//memcpy(new_array, listArray, list_size*sizeof(list_type));
// set old array to point to the newly allocated and twice as large array
delete[] listArray;
}
listArray = new_array;
}
}
} // End namespace
#endif

319
thirdparty/raknet/Source/DS_Map.h vendored Normal file
View File

@@ -0,0 +1,319 @@
/// \file
/// \brief \b [Internal] Map
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __RAKNET_MAP_H
#define __RAKNET_MAP_H
#include "DS_OrderedList.h"
#include "Export.h"
// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html
// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast.
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
/// The default comparison has to be first so it can be called as a default parameter.
/// It then is followed by MapNode, followed by NodeComparisonFunc
template <class key_type>
int defaultMapKeyComparison(const key_type &a, const key_type &b)
{
if (a<b) return -1; if (a==b) return 0; return 1;
}
/// \note IMPORTANT! If you use defaultMapKeyComparison then call IMPLEMENT_DEFAULT_COMPARISON or you will get an unresolved external linker error.
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&, const key_type&)=defaultMapKeyComparison<key_type> >
class RAK_DLL_EXPORT Map : public RakNet::RakMemoryOverride
{
public:
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<key_type>(key_type(),key_type());}
struct MapNode
{
MapNode() {}
MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {}
MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;}
MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;}
key_type mapNodeKey;
data_type mapNodeData;
};
// Has to be a static because the comparison callback for DataStructures::OrderedList is a C function
static int NodeComparisonFunc(const key_type &a, const MapNode &b)
{
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
return key_comparison_func(a, b.mapNodeKey);
}
Map();
~Map();
Map( const Map& original_copy );
Map& operator= ( const Map& original_copy );
data_type& Get(const key_type &key);
data_type Pop(const key_type &key);
// Add if needed
void Set(const key_type &key, const data_type &data);
// Must already exist
void SetExisting(const key_type &key, const data_type &data);
// Must add
void SetNew(const key_type &key, const data_type &data);
bool Has(const key_type &key);
bool Delete(const key_type &key);
data_type& operator[] ( const unsigned int position ) const;
key_type GetKeyAtIndex( const unsigned int position ) const;
unsigned GetIndexAtKey( const key_type &key );
void RemoveAtIndex(const unsigned index);
void Clear(void);
unsigned Size(void) const;
protected:
DataStructures::OrderedList< key_type,MapNode,Map::NodeComparisonFunc > mapNodeList;
void SaveLastSearch(const key_type &key, unsigned index);
bool HasSavedSearchResult(const key_type &key) const;
unsigned lastSearchIndex;
key_type lastSearchKey;
bool lastSearchIndexValid;
};
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
Map<key_type, data_type, key_comparison_func>::Map()
{
lastSearchIndexValid=false;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
Map<key_type, data_type, key_comparison_func>::~Map()
{
Clear();
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
Map<key_type, data_type, key_comparison_func>::Map( const Map& original_copy )
{
mapNodeList=original_copy.mapNodeList;
lastSearchIndex=original_copy.lastSearchIndex;
lastSearchKey=original_copy.lastSearchKey;
lastSearchIndexValid=original_copy.lastSearchIndexValid;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
Map<key_type, data_type, key_comparison_func>& Map<key_type, data_type, key_comparison_func>::operator= ( const Map& original_copy )
{
mapNodeList=original_copy.mapNodeList;
lastSearchIndex=original_copy.lastSearchIndex;
lastSearchKey=original_copy.lastSearchKey;
lastSearchIndexValid=original_copy.lastSearchIndexValid;
return *this;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
data_type& Map<key_type, data_type, key_comparison_func>::Get(const key_type &key)
{
if (HasSavedSearchResult(key))
return mapNodeList[lastSearchIndex].mapNodeData;
bool objectExists;
unsigned index;
index=mapNodeList.GetIndexFromKey(key, &objectExists);
assert(objectExists);
SaveLastSearch(key,index);
return mapNodeList[index].mapNodeData;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
unsigned Map<key_type, data_type, key_comparison_func>::GetIndexAtKey( const key_type &key )
{
if (HasSavedSearchResult(key))
return lastSearchIndex;
bool objectExists;
unsigned index;
index=mapNodeList.GetIndexFromKey(key, &objectExists);
if (objectExists==false)
{
assert(objectExists);
}
SaveLastSearch(key,index);
return index;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::RemoveAtIndex(const unsigned index)
{
mapNodeList.RemoveAtIndex(index);
lastSearchIndexValid=false;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
data_type Map<key_type, data_type, key_comparison_func>::Pop(const key_type &key)
{
bool objectExists;
unsigned index;
if (HasSavedSearchResult(key))
index=lastSearchIndex;
else
{
index=mapNodeList.GetIndexFromKey(key, &objectExists);
assert(objectExists);
}
data_type tmp = mapNodeList[index].mapNodeData;
mapNodeList.RemoveAtIndex(index);
lastSearchIndexValid=false;
return tmp;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::Set(const key_type &key, const data_type &data)
{
bool objectExists;
unsigned index;
if (HasSavedSearchResult(key))
{
mapNodeList[lastSearchIndex].mapNodeData=data;
return;
}
index=mapNodeList.GetIndexFromKey(key, &objectExists);
if (objectExists)
{
SaveLastSearch(key,index);
mapNodeList[index].mapNodeData=data;
}
else
{
SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true));
}
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::SetExisting(const key_type &key, const data_type &data)
{
bool objectExists;
unsigned index;
if (HasSavedSearchResult(key))
{
index=lastSearchIndex;
}
else
{
index=mapNodeList.GetIndexFromKey(key, &objectExists);
assert(objectExists);
SaveLastSearch(key,index);
}
mapNodeList[index].mapNodeData=data;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::SetNew(const key_type &key, const data_type &data)
{
#ifdef _DEBUG
unsigned index;
bool objectExists;
index=mapNodeList.GetIndexFromKey(key, &objectExists);
assert(objectExists==false);
#endif
SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true));
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
bool Map<key_type, data_type, key_comparison_func>::Has(const key_type &key)
{
if (HasSavedSearchResult(key))
return true;
bool objectExists;
unsigned index;
index=mapNodeList.GetIndexFromKey(key, &objectExists);
if (objectExists)
SaveLastSearch(key,index);
return objectExists;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
bool Map<key_type, data_type, key_comparison_func>::Delete(const key_type &key)
{
if (HasSavedSearchResult(key))
{
lastSearchIndexValid=false;
mapNodeList.RemoveAtIndex(lastSearchIndex);
return true;
}
bool objectExists;
unsigned index;
index=mapNodeList.GetIndexFromKey(key, &objectExists);
if (objectExists)
{
lastSearchIndexValid=false;
mapNodeList.RemoveAtIndex(index);
return true;
}
else
return false;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::Clear(void)
{
lastSearchIndexValid=false;
mapNodeList.Clear();
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
data_type& Map<key_type, data_type, key_comparison_func>::operator[]( const unsigned int position ) const
{
return mapNodeList[position].mapNodeData;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
key_type Map<key_type, data_type, key_comparison_func>::GetKeyAtIndex( const unsigned int position ) const
{
return mapNodeList[position].mapNodeKey;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
unsigned Map<key_type, data_type, key_comparison_func>::Size(void) const
{
return mapNodeList.Size();
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
void Map<key_type, data_type, key_comparison_func>::SaveLastSearch(const key_type &key, const unsigned index)
{
lastSearchIndex=index;
lastSearchKey=key;
lastSearchIndexValid=true;
}
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
bool Map<key_type, data_type, key_comparison_func>::HasSavedSearchResult(const key_type &key) const
{
return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0;
}
}
#endif

285
thirdparty/raknet/Source/DS_MemoryPool.h vendored Normal file
View File

@@ -0,0 +1,285 @@
#ifndef __MEMORY_POOL_H
#define __MEMORY_POOL_H
#ifndef __APPLE__
// Use stdlib and not malloc for compatibility
#include <stdlib.h>
#endif
#include <assert.h>
#include "Export.h"
#include "RakMemoryOverride.h"
// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1
#define DS_MEMORY_POOL_MAX_FREE_PAGES 4
// #define _DISABLE_MEMORY_POOL
namespace DataStructures
{
/// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors.
/// Contains a list of pages, each of which has an array of the user structures
template <class MemoryBlockType>
class RAK_DLL_EXPORT MemoryPool : public RakNet::RakMemoryOverride
{
public:
struct Page;
struct MemoryWithPage
{
MemoryBlockType userMemory;
Page *parentPage;
};
struct Page
{
MemoryWithPage** availableStack;
int availableStackSize;
MemoryWithPage* block;
Page *next, *prev;
};
MemoryPool();
~MemoryPool();
void SetPageSize(int size); // Defaults to 16384
MemoryBlockType *Allocate(void);
void Release(MemoryBlockType *m);
void Clear(void);
int GetAvailablePagesSize(void) const {return availablePagesSize;}
int GetUnavailablePagesSize(void) const {return unavailablePagesSize;}
int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;}
protected:
int BlocksPerPage(void) const;
void AllocateFirst(void);
bool InitPage(Page *page, Page *prev);
// availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list
// unavailablePages are pages which are totally full, and from which we do not return new blocks.
// Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages
Page *availablePages, *unavailablePages;
int availablePagesSize, unavailablePagesSize;
int memoryPoolPageSize;
};
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::MemoryPool()
{
#ifndef _DISABLE_MEMORY_POOL
//AllocateFirst();
availablePagesSize=0;
unavailablePagesSize=0;
memoryPoolPageSize=16384;
#endif
}
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::~MemoryPool()
{
#ifndef _DISABLE_MEMORY_POOL
Clear();
#endif
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::SetPageSize(int size)
{
memoryPoolPageSize=size;
}
template<class MemoryBlockType>
MemoryBlockType* MemoryPool<MemoryBlockType>::Allocate(void)
{
#ifdef _DISABLE_MEMORY_POOL
return new MemoryBlockType;
#endif
if (availablePagesSize>0)
{
MemoryBlockType *retVal;
Page *curPage;
curPage=availablePages;
retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)];
if (curPage->availableStackSize==0)
{
--availablePagesSize;
availablePages=curPage->next;
assert(availablePagesSize==0 || availablePages->availableStackSize>0);
curPage->next->prev=curPage->prev;
curPage->prev->next=curPage->next;
if (unavailablePagesSize++==0)
{
unavailablePages=curPage;
curPage->next=curPage;
curPage->prev=curPage;
}
else
{
curPage->next=unavailablePages;
curPage->prev=unavailablePages->prev;
unavailablePages->prev->next=curPage;
unavailablePages->prev=curPage;
}
}
assert(availablePagesSize==0 || availablePages->availableStackSize>0);
return retVal;
}
availablePages = (Page *) rakMalloc(sizeof(Page));
if (availablePages==0)
return 0;
availablePagesSize=1;
if (InitPage(availablePages, availablePages)==false)
return 0;
assert(availablePages->availableStackSize>1);
return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize];
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Release(MemoryBlockType *m)
{
#ifdef _DISABLE_MEMORY_POOL
delete m;
return;
#endif
// Find the page this block is in and return it.
Page *curPage;
MemoryWithPage *memoryWithPage = (MemoryWithPage*)m;
curPage=memoryWithPage->parentPage;
if (curPage->availableStackSize==0)
{
// The page is in the unavailable list so move it to the available list
curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
unavailablePagesSize--;
// As this page is no longer totally empty, move it to the end of available pages
curPage->next->prev=curPage->prev;
curPage->prev->next=curPage->next;
if (unavailablePagesSize>0 && curPage==unavailablePages)
unavailablePages=unavailablePages->next;
if (availablePagesSize++==0)
{
availablePages=curPage;
curPage->next=curPage;
curPage->prev=curPage;
}
else
{
curPage->next=availablePages;
curPage->prev=availablePages->prev;
availablePages->prev->next=curPage;
availablePages->prev=curPage;
}
}
else
{
curPage->availableStack[curPage->availableStackSize++]=memoryWithPage;
if (curPage->availableStackSize==BlocksPerPage() &&
availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES)
{
// After a certain point, just deallocate empty pages rather than keep them around
if (curPage==availablePages)
{
availablePages=curPage->next;
assert(availablePages->availableStackSize>0);
}
curPage->prev->next=curPage->next;
curPage->next->prev=curPage->prev;
availablePagesSize--;
rakFree(curPage->availableStack);
rakFree(curPage->block);
rakFree(curPage);
}
}
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Clear(void)
{
#ifdef _DISABLE_MEMORY_POOL
return;
#endif
Page *cur, *freed;
if (availablePagesSize>0)
{
cur = availablePages;
#ifdef _MSC_VER
#pragma warning(disable:4127) // conditional expression is constant
#endif
while (true)
// do
{
rakFree(cur->availableStack);
rakFree(cur->block);
freed=cur;
cur=cur->next;
if (cur==availablePages)
{
rakFree(freed);
break;
}
rakFree(freed);
}// while(cur!=availablePages);
}
if (unavailablePagesSize>0)
{
cur = unavailablePages;
while (1)
//do
{
rakFree(cur->availableStack);
rakFree(cur->block);
freed=cur;
cur=cur->next;
if (cur==unavailablePages)
{
rakFree(freed);
break;
}
rakFree(freed);
} // while(cur!=unavailablePages);
}
availablePagesSize=0;
unavailablePagesSize=0;
}
template<class MemoryBlockType>
int MemoryPool<MemoryBlockType>::BlocksPerPage(void) const
{
return memoryPoolPageSize / sizeof(MemoryWithPage);
}
template<class MemoryBlockType>
bool MemoryPool<MemoryBlockType>::InitPage(Page *page, Page *prev)
{
int i=0;
const int bpp = BlocksPerPage();
page->block=(MemoryWithPage*) rakMalloc(memoryPoolPageSize);
if (page->block==0)
return false;
page->availableStack=(MemoryWithPage**)rakMalloc(sizeof(MemoryWithPage*)*bpp);
if (page->availableStack==0)
{
rakFree(page->block);
return false;
}
MemoryWithPage *curBlock = page->block;
MemoryWithPage **curStack = page->availableStack;
while (i < bpp)
{
curBlock->parentPage=page;
curStack[i]=curBlock++;
i++;
}
page->availableStackSize=bpp;
page->next=availablePages;
page->prev=prev;
return true;
}
}
#endif

View File

@@ -0,0 +1,252 @@
/// \file
/// \brief \b [Internal] Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H
#define __RAKNET_ORDERED_CHANNEL_HEAP_H
#include "DS_Heap.h"
#include "DS_Map.h"
#include "DS_Queue.h"
#include "Export.h"
#include <assert.h>
#include "Rand.h"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)=defaultMapKeyComparison<channel_key_type> >
class RAK_DLL_EXPORT OrderedChannelHeap : public RakNet::RakMemoryOverride
{
public:
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<channel_key_type>(channel_key_type(),channel_key_type());}
OrderedChannelHeap();
~OrderedChannelHeap();
void Push(const channel_key_type &channelID, const heap_data_type &data);
void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data);
heap_data_type Pop(const unsigned startingIndex=0);
heap_data_type Peek(const unsigned startingIndex) const;
void AddChannel(const channel_key_type &channelID, const double weight);
void RemoveChannel(channel_key_type channelID);
void Clear(void);
heap_data_type& operator[] ( const unsigned int position ) const;
unsigned ChannelSize(const channel_key_type &channelID);
unsigned Size(void) const;
struct QueueAndWeight
{
DataStructures::Queue<double> randResultQueue;
double weight;
bool signalDeletion;
};
struct HeapChannelAndData
{
HeapChannelAndData() {}
HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {}
heap_data_type data;
channel_key_type channel;
};
protected:
DataStructures::Map<channel_key_type, QueueAndWeight*, channel_key_comparison_func> map;
DataStructures::Heap<double, HeapChannelAndData, true> heap;
void GreatestRandResult(void);
};
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::OrderedChannelHeap()
{
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::~OrderedChannelHeap()
{
Clear();
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Push(const channel_key_type &channelID, const heap_data_type &data)
{
PushAtHead(MAX_UNSIGNED_LONG, channelID, data);
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::GreatestRandResult(void)
{
double greatest;
unsigned i;
greatest=0.0;
for (i=0; i < map.Size(); i++)
{
if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest)
greatest=map[i]->randResultQueue[0];
}
return greatest;
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data)
{
// If an assert hits here then this is an unknown channel. Call AddChannel first.
QueueAndWeight *queueAndWeight=map.Get(channelID);
double maxRange, minRange, rnd;
if (queueAndWeight->randResultQueue.Size()==0)
{
// Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily
// This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25
// and then we added another channel, the new channel would need to choose between .25 and 0
// If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be
maxRange=GreatestRandResult();
if (maxRange==0.0)
maxRange=1.0;
minRange=0.0;
}
else if (index >= queueAndWeight->randResultQueue.Size())
{
maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999;
minRange=0.0;
}
else
{
if (index==0)
{
maxRange=GreatestRandResult();
if (maxRange==queueAndWeight->randResultQueue[0])
maxRange=1.0;
}
else if (index >= queueAndWeight->randResultQueue.Size())
maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999;
else
maxRange=queueAndWeight->randResultQueue[index-1]*.99999999;
minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001;
}
#ifdef _DEBUG
assert(maxRange!=0.0);
#endif
rnd=frandomMT() * (maxRange - minRange);
if (rnd==0.0)
rnd=maxRange/2.0;
if (index >= queueAndWeight->randResultQueue.Size())
queueAndWeight->randResultQueue.Push(rnd);
else
queueAndWeight->randResultQueue.PushAtHead(rnd, index);
heap.Push(rnd*queueAndWeight->weight, HeapChannelAndData(channelID, data));
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
heap_data_type OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Pop(const unsigned startingIndex)
{
assert(startingIndex < heap.Size());
QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel);
if (startingIndex!=0)
{
// Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue
unsigned indiceCount=0;
unsigned i;
for (i=0; i < startingIndex; i++)
if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0)
indiceCount++;
queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount);
}
else
{
// TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop.
queueAndWeight->randResultQueue.Pop();
}
// Try to remove the channel after every pop, because doing so is not valid while there are elements in the list.
if (queueAndWeight->signalDeletion)
RemoveChannel(heap[startingIndex].channel);
return heap.Pop(startingIndex).data;
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
heap_data_type OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Peek(const unsigned startingIndex) const
{
HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex);
return heapChannelAndData.data;
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::AddChannel(const channel_key_type &channelID, const double weight)
{
QueueAndWeight *qaw = new QueueAndWeight;
qaw->weight=weight;
qaw->signalDeletion=false;
map.SetNew(channelID, qaw);
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::RemoveChannel(channel_key_type channelID)
{
if (map.Has(channelID))
{
unsigned i;
i=map.GetIndexAtKey(channelID);
if (map[i]->randResultQueue.Size()==0)
{
delete map[i];
map.RemoveAtIndex(i);
}
else
{
// Signal this channel for deletion later, because the heap has nodes with this channel right now
map[i]->signalDeletion=true;
}
}
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
unsigned OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Size(void) const
{
return heap.Size();
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
heap_data_type& OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::operator[]( const unsigned int position ) const
{
return heap[position].data;
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
unsigned OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::ChannelSize(const channel_key_type &channelID)
{
QueueAndWeight *queueAndWeight=map.Get(channelID);
return queueAndWeight->randResultQueue.Size();
}
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Clear(void)
{
unsigned i;
for (i=0; i < map.Size(); i++)
delete map[i];
map.Clear();
heap.Clear();
}
}
#endif

View File

@@ -0,0 +1,271 @@
/// \file
/// \brief \b [Internal] Quicksort ordered list.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "DS_List.h"
#include "RakMemoryOverride.h"
#include "Export.h"
#ifndef __ORDERED_LIST_H
#define __ORDERED_LIST_H
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
template <class key_type, class data_type>
int defaultOrderedListComparison(const key_type &a, const data_type &b)
{
if (a<b) return -1; if (a==b) return 0; return 1;
}
/// \note IMPORTANT! If you use defaultOrderedListComparison then call IMPLEMENT_DEFAULT_COMPARISON or you will get an unresolved external linker error.
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)=defaultOrderedListComparison<key_type, data_type> >
class RAK_DLL_EXPORT OrderedList : public RakNet::RakMemoryOverride
{
public:
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison<key_type, data_type>(key_type(),data_type());}
OrderedList();
~OrderedList();
OrderedList( const OrderedList& original_copy );
OrderedList& operator= ( const OrderedList& original_copy );
/// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0
/// If the data type has comparison operators already defined then you can just use defaultComparison
bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
// GetIndexFromKey returns where the insert should go at the same time checks if it is there
unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, int (*cf)(const key_type&, const data_type&)=default_comparison_function);
unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function);
unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function);
data_type& operator[] ( const unsigned int position ) const;
void RemoveAtIndex(const unsigned index);
void InsertAtIndex(const data_type &data, const unsigned index);
void InsertAtEnd(const data_type &data);
void RemoveFromEnd(const unsigned num=1);
void Clear(bool doNotDeallocate=false);
unsigned Size(void) const;
protected:
DataStructures::List<data_type> orderedList;
};
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, default_comparison_function>::OrderedList()
{
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, default_comparison_function>::~OrderedList()
{
Clear();
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, default_comparison_function>::OrderedList( const OrderedList& original_copy )
{
orderedList=original_copy.orderedList;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, default_comparison_function>& OrderedList<key_type, data_type, default_comparison_function>::operator= ( const OrderedList& original_copy )
{
orderedList=original_copy.orderedList;
return *this;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
bool OrderedList<key_type, data_type, default_comparison_function>::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
return objectExists;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
data_type OrderedList<key_type, data_type, default_comparison_function>::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
assert(objectExists);
return orderedList[index];
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
bool OrderedList<key_type, data_type, default_comparison_function>::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
if (objectExists)
element = orderedList[index];
return objectExists;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, default_comparison_function>::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const
{
int index, upperBound, lowerBound;
int res;
if (orderedList.Size()==0)
{
*objectExists=false;
return 0;
}
upperBound=(int)orderedList.Size()-1;
lowerBound=0;
index = (int)orderedList.Size()/2;
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while (1)
{
res = cf(key,orderedList[index]);
if (res==0)
{
*objectExists=true;
return index;
}
else if (res<0)
{
upperBound=index-1;
}
else// if (res>0)
{
lowerBound=index+1;
}
index=lowerBound+(upperBound-lowerBound)/2;
if (lowerBound>upperBound)
{
*objectExists=false;
return lowerBound; // No match
}
}
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, default_comparison_function>::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, int (*cf)(const key_type&, const data_type&))
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
// Don't allow duplicate insertion.
if (objectExists)
{
// This is usually a bug! Use InsertAllowDuplicate if you want duplicates
assert(assertOnDuplicate==false);
return (unsigned)-1;
}
if (index>=orderedList.Size())
{
orderedList.Insert(data);
return orderedList.Size()-1;
}
else
{
orderedList.Insert(data,index);
return index;
}
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, default_comparison_function>::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&))
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
// Can't find the element to remove if this assert hits
// assert(objectExists==true);
if (objectExists==false)
{
assert(objectExists==true);
return 0;
}
orderedList.RemoveAtIndex(index);
return index;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, default_comparison_function>::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&))
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists, cf);
// Can't find the element to remove if this assert hits
if (objectExists==false)
return 0;
orderedList.RemoveAtIndex(index);
return index;
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, default_comparison_function>::RemoveAtIndex(const unsigned index)
{
orderedList.RemoveAtIndex(index);
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, default_comparison_function>::InsertAtIndex(const data_type &data, const unsigned index)
{
orderedList.Insert(data, index);
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, default_comparison_function>::InsertAtEnd(const data_type &data)
{
orderedList.Insert(data);
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, default_comparison_function>::RemoveFromEnd(const unsigned num)
{
orderedList.RemoveFromEnd(num);
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, default_comparison_function>::Clear(bool doNotDeallocate)
{
orderedList.Clear(doNotDeallocate);
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
data_type& OrderedList<key_type, data_type, default_comparison_function>::operator[]( const unsigned int position ) const
{
return orderedList[position];
}
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, default_comparison_function>::Size(void) const
{
return orderedList.Size();
}
}
#endif

418
thirdparty/raknet/Source/DS_Queue.h vendored Normal file
View File

@@ -0,0 +1,418 @@
/// \file
/// \brief \b [Internal] A queue used by RakNet.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __QUEUE_H
#define __QUEUE_H
// Template classes have to have all the code in the header file
#include <assert.h>
#include "Export.h"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
/// \brief A queue implemented as an array with a read and write index.
template <class queue_type>
class RAK_DLL_EXPORT Queue
{
public:
Queue();
~Queue();
Queue( Queue& original_copy );
bool operator= ( const Queue& original_copy );
void Push( const queue_type& input );
void PushAtHead( const queue_type& input, unsigned index=0 );
queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency
void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency
inline queue_type Peek( void ) const;
inline queue_type PeekTail( void ) const;
inline queue_type Pop( void );
inline unsigned int Size( void ) const;
inline bool IsEmpty(void) const;
inline unsigned int AllocationSize( void ) const;
inline void Clear( void );
void Compress( void );
bool Find ( queue_type q );
void ClearAndForceAllocation( int size ); // Force a memory allocation to a certain larger size
private:
queue_type* array;
unsigned int head; // Array index for the head of the queue
unsigned int tail; // Array index for the tail of the queue
unsigned int allocation_size;
};
template <class queue_type>
inline unsigned int Queue<queue_type>::Size( void ) const
{
if ( head <= tail )
return tail -head;
else
return allocation_size -head + tail;
}
template <class queue_type>
inline bool Queue<queue_type>::IsEmpty(void) const
{
return head==tail;
}
template <class queue_type>
inline unsigned int Queue<queue_type>::AllocationSize( void ) const
{
return allocation_size;
}
template <class queue_type>
Queue<queue_type>::Queue()
{
allocation_size = 16;
array = new queue_type[ allocation_size ];
head = 0;
tail = 0;
}
template <class queue_type>
Queue<queue_type>::~Queue()
{
if (allocation_size>0)
delete [] array;
}
template <class queue_type>
inline queue_type Queue<queue_type>::Pop( void )
{
#ifdef _DEBUG
assert( allocation_size > 0 && Size() >= 0 && head != tail);
#endif
//head=(head+1) % allocation_size;
if ( ++head == allocation_size )
head = 0;
if ( head == 0 )
return ( queue_type ) array[ allocation_size -1 ];
return ( queue_type ) array[ head -1 ];
}
template <class queue_type>
void Queue<queue_type>::PushAtHead( const queue_type& input, unsigned index )
{
assert(index <= Size());
// Just force a reallocation, will be overwritten
Push(input);
if (Size()==1)
return;
unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex;
writeIndex=Size()-1;
readIndex=writeIndex-1;
while (readIndex >= index)
{
if ( head + writeIndex >= allocation_size )
trueWriteIndex = head + writeIndex - allocation_size;
else
trueWriteIndex = head + writeIndex;
if ( head + readIndex >= allocation_size )
trueReadIndex = head + readIndex - allocation_size;
else
trueReadIndex = head + readIndex;
array[trueWriteIndex]=array[trueReadIndex];
if (readIndex==0)
break;
writeIndex--;
readIndex--;
}
if ( head + index >= allocation_size )
trueWriteIndex = head + index - allocation_size;
else
trueWriteIndex = head + index;
array[trueWriteIndex]=input;
}
template <class queue_type>
inline queue_type Queue<queue_type>::Peek( void ) const
{
#ifdef _DEBUG
assert( head != tail );
assert( allocation_size > 0 && Size() >= 0 );
#endif
return ( queue_type ) array[ head ];
}
template <class queue_type>
inline queue_type Queue<queue_type>::PeekTail( void ) const
{
#ifdef _DEBUG
assert( head != tail );
assert( allocation_size > 0 && Size() >= 0 );
#endif
if (tail!=0)
return ( queue_type ) array[ tail-1 ];
else
return ( queue_type ) array[ allocation_size-1 ];
}
template <class queue_type>
void Queue<queue_type>::Push( const queue_type& input )
{
if ( allocation_size == 0 )
{
array = new queue_type[ 16 ];
head = 0;
tail = 1;
array[ 0 ] = input;
allocation_size = 16;
return ;
}
array[ tail++ ] = input;
if ( tail == allocation_size )
tail = 0;
if ( tail == head )
{
// unsigned int index=tail;
// Need to allocate more memory.
queue_type * new_array;
new_array = new queue_type[ allocation_size * 2 ];
#ifdef _DEBUG
assert( new_array );
#endif
if (new_array==0)
return;
for ( unsigned int counter = 0; counter < allocation_size; ++counter )
new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ];
head = 0;
tail = allocation_size;
allocation_size *= 2;
// Delete the old array and move the pointer to the new array
delete [] array;
array = new_array;
}
}
template <class queue_type>
Queue<queue_type>::Queue( Queue& original_copy )
{
// Allocate memory for copy
if ( original_copy.Size() == 0 )
{
allocation_size = 0;
}
else
{
array = new queue_type [ original_copy.Size() + 1 ];
for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter )
array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ];
head = 0;
tail = original_copy.Size();
allocation_size = original_copy.Size() + 1;
}
}
template <class queue_type>
bool Queue<queue_type>::operator= ( const Queue& original_copy )
{
if ( ( &original_copy ) == this )
return false;
Clear();
// Allocate memory for copy
if ( original_copy.Size() == 0 )
{
allocation_size = 0;
}
else
{
array = new queue_type [ original_copy.Size() + 1 ];
for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter )
array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ];
head = 0;
tail = original_copy.Size();
allocation_size = original_copy.Size() + 1;
}
return true;
}
template <class queue_type>
inline void Queue<queue_type>::Clear ( void )
{
if ( allocation_size == 0 )
return ;
if (allocation_size > 32)
{
delete[] array;
allocation_size = 0;
}
head = 0;
tail = 0;
}
template <class queue_type>
void Queue<queue_type>::Compress ( void )
{
queue_type* new_array;
unsigned int newAllocationSize;
if (allocation_size==0)
return;
newAllocationSize=1;
while (newAllocationSize <= Size())
newAllocationSize<<=1; // Must be a better way to do this but I'm too dumb to figure it out quickly :)
new_array = new queue_type [newAllocationSize];
for (unsigned int counter=0; counter < Size(); ++counter)
new_array[counter] = array[(head + counter)%(allocation_size)];
tail=Size();
allocation_size=newAllocationSize;
head=0;
// Delete the old array and move the pointer to the new array
delete [] array;
array=new_array;
}
template <class queue_type>
bool Queue<queue_type>::Find ( queue_type q )
{
if ( allocation_size == 0 )
return false;
unsigned int counter = head;
while ( counter != tail )
{
if ( array[ counter ] == q )
return true;
counter = ( counter + 1 ) % allocation_size;
}
return false;
}
template <class queue_type>
void Queue<queue_type>::ClearAndForceAllocation( int size )
{
delete [] array;
array = new queue_type[ size ];
allocation_size = size;
head = 0;
tail = 0;
}
template <class queue_type>
inline queue_type& Queue<queue_type>::operator[] ( unsigned int position ) const
{
#ifdef _DEBUG
assert( position < Size() );
#endif
//return array[(head + position) % allocation_size];
if ( head + position >= allocation_size )
return array[ head + position - allocation_size ];
else
return array[ head + position ];
}
template <class queue_type>
void Queue<queue_type>::RemoveAtIndex( unsigned int position )
{
#ifdef _DEBUG
assert( position < Size() );
assert( head != tail );
#endif
if ( head == tail || position >= Size() )
return ;
unsigned int index;
unsigned int next;
//index = (head + position) % allocation_size;
if ( head + position >= allocation_size )
index = head + position - allocation_size;
else
index = head + position;
//next = (index + 1) % allocation_size;
next = index + 1;
if ( next == allocation_size )
next = 0;
while ( next != tail )
{
// Overwrite the previous element
array[ index ] = array[ next ];
index = next;
//next = (next + 1) % allocation_size;
if ( ++next == allocation_size )
next = 0;
}
// Move the tail back
if ( tail == 0 )
tail = allocation_size - 1;
else
--tail;
}
} // End namespace
#endif

View File

@@ -0,0 +1,111 @@
/// \file
/// \brief \b [Internal] A queue implemented as a linked list.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __QUEUE_LINKED_LIST_H
#define __QUEUE_LINKED_LIST_H
#include "DS_LinkedList.h"
#include "Export.h"
#include "RakMemoryOverride.h"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
/// \brief A queue implemented using a linked list. Rarely used.
template <class QueueType>
class RAK_DLL_EXPORT QueueLinkedList : public RakNet::RakMemoryOverride
{
public:
QueueLinkedList();
QueueLinkedList( const QueueLinkedList& original_copy );
bool operator= ( const QueueLinkedList& original_copy );
QueueType Pop( void );
QueueType& Peek( void );
QueueType& EndPeek( void );
void Push( const QueueType& input );
unsigned int Size( void );
void Clear( void );
void Compress( void );
private:
LinkedList<QueueType> data;
};
template <class QueueType>
QueueLinkedList<QueueType>::QueueLinkedList()
{
}
template <class QueueType>
inline unsigned int QueueLinkedList<QueueType>::Size()
{
return data.Size();
}
template <class QueueType>
inline QueueType QueueLinkedList<QueueType>::Pop( void )
{
data.Beginning();
return ( QueueType ) data.Pop();
}
template <class QueueType>
inline QueueType& QueueLinkedList<QueueType>::Peek( void )
{
data.Beginning();
return ( QueueType ) data.Peek();
}
template <class QueueType>
inline QueueType& QueueLinkedList<QueueType>::EndPeek( void )
{
data.End();
return ( QueueType ) data.Peek();
}
template <class QueueType>
void QueueLinkedList<QueueType>::Push( const QueueType& input )
{
data.End();
data.Add( input );
}
template <class QueueType>
QueueLinkedList<QueueType>::QueueLinkedList( const QueueLinkedList& original_copy )
{
data = original_copy.data;
}
template <class QueueType>
bool QueueLinkedList<QueueType>::operator= ( const QueueLinkedList& original_copy )
{
if ( ( &original_copy ) == this )
return false;
data = original_copy.data;
}
template <class QueueType>
void QueueLinkedList<QueueType>::Clear ( void )
{
data.Clear();
}
} // End namespace
#endif

219
thirdparty/raknet/Source/DS_RangeList.h vendored Normal file
View File

@@ -0,0 +1,219 @@
#ifndef __RANGE_LIST_H
#define __RANGE_LIST_H
#include "DS_OrderedList.h"
#include "BitStream.h"
#include "RakMemoryOverride.h"
#include <assert.h>
namespace DataStructures
{
template <class range_type>
struct RangeNode : public RakNet::RakMemoryOverride
{
RangeNode() {}
~RangeNode() {}
RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;}
range_type minIndex;
range_type maxIndex;
};
template <class range_type>
int RangeNodeComp(const range_type &a, const RangeNode<range_type> &b)
{
if (a<b.minIndex)
return -1;
if (a==b.minIndex)
return 0;
return 1;
}
template <class range_type>
class RAK_DLL_EXPORT RangeList : public RakNet::RakMemoryOverride
{
public:
RangeList();
~RangeList();
void Insert(range_type index);
void Clear(void);
unsigned Size(void) const;
unsigned RangeSum(void) const;
BitSize_t Serialize(RakNet::BitStream *in, BitSize_t maxBits, bool clearSerialized);
bool Deserialize(RakNet::BitStream *out);
DataStructures::OrderedList<range_type, RangeNode<range_type> , RangeNodeComp<range_type> > ranges;
};
template <class range_type>
BitSize_t RangeList<range_type>::Serialize(RakNet::BitStream *in, BitSize_t maxBits, bool clearSerialized)
{
assert(ranges.Size() < (unsigned short)-1);
RakNet::BitStream tempBS;
BitSize_t bitsWritten;
unsigned short countWritten;
unsigned i;
countWritten=0;
bitsWritten=0;
for (i=0; i < ranges.Size(); i++)
{
if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits)
break;
tempBS.Write(ranges[i].minIndex==ranges[i].maxIndex);
tempBS.Write(ranges[i].minIndex);
bitsWritten+=sizeof(range_type)*8+1;
if (ranges[i].minIndex!=ranges[i].maxIndex)
{
tempBS.Write(ranges[i].maxIndex);
bitsWritten+=sizeof(range_type)*8;
}
countWritten++;
}
BitSize_t before=in->GetWriteOffset();
in->WriteCompressed(countWritten);
bitsWritten+=in->GetWriteOffset()-before;
// printf("%i ", in->GetNumberOfBitsUsed());
in->Write(&tempBS, tempBS.GetNumberOfBitsUsed());
// printf("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed());
if (clearSerialized && countWritten)
{
unsigned rangeSize=ranges.Size();
for (i=0; i < rangeSize-countWritten; i++)
{
ranges[i]=ranges[i+countWritten];
}
ranges.RemoveFromEnd(countWritten);
}
return bitsWritten;
}
template <class range_type>
bool RangeList<range_type>::Deserialize(RakNet::BitStream *out)
{
ranges.Clear();
unsigned short count;
out->ReadCompressed(count);
unsigned short i;
range_type min,max;
bool maxEqualToMin=false;
for (i=0; i < count; i++)
{
out->Read(maxEqualToMin);
if (out->Read(min)==false)
return false;
if (maxEqualToMin==false)
{
if (out->Read(max)==false)
return false;
if (max<min)
return false;
}
else
max=min;
ranges.InsertAtEnd(RangeNode<range_type>(min,max));
}
return true;
}
template <class range_type>
RangeList<range_type>::RangeList()
{
RangeNodeComp<range_type>(0, RangeNode<range_type>());
}
template <class range_type>
RangeList<range_type>::~RangeList()
{
Clear();
}
template <class range_type>
void RangeList<range_type>::Insert(range_type index)
{
if (ranges.Size()==0)
{
ranges.Insert(index, RangeNode<range_type>(index, index), true);
return;
}
bool objectExists;
unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists);
if (insertionIndex==ranges.Size())
{
if (index == ranges[insertionIndex-1].maxIndex+1)
ranges[insertionIndex-1].maxIndex++;
else if (index > ranges[insertionIndex-1].maxIndex+1)
{
// Insert at end
ranges.Insert(index, RangeNode<range_type>(index, index), true);
}
return;
}
if (index < ranges[insertionIndex].minIndex-1)
{
// Insert here
ranges.InsertAtIndex(RangeNode<range_type>(index, index), insertionIndex);
return;
}
else if (index == ranges[insertionIndex].minIndex-1)
{
// Decrease minIndex and join left
ranges[insertionIndex].minIndex--;
if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+1==ranges[insertionIndex].minIndex)
{
ranges[insertionIndex-1].maxIndex=ranges[insertionIndex].maxIndex;
ranges.RemoveAtIndex(insertionIndex);
}
return;
}
else if (index >= ranges[insertionIndex].minIndex && index <= ranges[insertionIndex].maxIndex)
{
// Already exists
return;
}
else if (index == ranges[insertionIndex].maxIndex+1)
{
// Increase maxIndex and join right
ranges[insertionIndex].maxIndex++;
if (insertionIndex<ranges.Size()-1 && ranges[insertionIndex+1].minIndex==ranges[insertionIndex].maxIndex+1)
{
ranges[insertionIndex+1].minIndex=ranges[insertionIndex].minIndex;
ranges.RemoveAtIndex(insertionIndex);
}
return;
}
}
template <class range_type>
void RangeList<range_type>::Clear(void)
{
ranges.Clear();
}
template <class range_type>
unsigned RangeList<range_type>::Size(void) const
{
return ranges.Size();
}
template <class range_type>
unsigned RangeList<range_type>::RangeSum(void) const
{
unsigned sum=0,i;
for (i=0; i < ranges.Size(); i++)
sum+=ranges[i].maxIndex-ranges[i].minIndex+1;
return sum;
}
}
#endif

1027
thirdparty/raknet/Source/DS_Table.cpp vendored Normal file

File diff suppressed because it is too large Load Diff

322
thirdparty/raknet/Source/DS_Table.h vendored Normal file
View File

@@ -0,0 +1,322 @@
#ifndef __TABLE_H
#define __TABLE_H
#ifdef _MSC_VER
#pragma warning( push )
#endif
#include "DS_List.h"
#include "DS_BPlusTree.h"
#include "RakMemoryOverride.h"
#include "Export.h"
#include "RakString.h"
#define _TABLE_BPLUS_TREE_ORDER 16
#define _TABLE_MAX_COLUMN_NAME_LENGTH 32
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
/// \brief Holds a set of columns, a set of rows, and rows times columns cells.
/// The table data structure is useful if you want to store a set of structures and perform queries on those structures
/// This is a relatively simple and fast implementation of the types of tables commonly used in databases
/// See TableSerializer to serialize data members of the table
/// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network.
class RAK_DLL_EXPORT Table : public RakNet::RakMemoryOverride
{
public:
enum ColumnType
{
// Cell::i used
NUMERIC,
// Cell::c used to hold a null terminated string.
STRING,
// Cell::c holds data. Cell::i holds data length of c in bytes.
BINARY,
// Cell::c holds data. Not deallocated. Set manually by assigning ptr.
POINTER,
};
/// Holds the actual data in the table
struct RAK_DLL_EXPORT Cell
{
Cell();
~Cell();
Cell(int intValue, char *charValue, void *ptr, ColumnType type);
void Clear(void);
/// Numeric
void Set(int input);
/// String
void Set(const char *input);
/// Binary
void Set(const char *input, int inputLength);
/// Pointer
void SetPtr(void* p);
/// Numeric
void Get(int *output);
/// String
void Get(char *output);
/// Binary
void Get(char *output, int *outputLength);
RakNet::RakString ToString(ColumnType columnType);
// assignment operator and copy constructor
Cell& operator = ( const Cell& input );
Cell( const Cell & input);
bool isEmpty;
int i;
char *c;
void *ptr;
};
/// Stores the name and type of the column
/// \internal
struct RAK_DLL_EXPORT ColumnDescriptor
{
ColumnDescriptor();
~ColumnDescriptor();
ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct);
char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH];
ColumnType columnType;
};
/// Stores the list of cells for this row, and a special flag used for internal sorting
struct RAK_DLL_EXPORT Row
{
// list of cells
DataStructures::List<Cell*> cells;
/// Numeric
void UpdateCell(unsigned columnIndex, int value);
/// String
void UpdateCell(unsigned columnIndex, const char *str);
/// Binary
void UpdateCell(unsigned columnIndex, int byteLength, const char *data);
};
// Operations to perform for cell comparison
enum FilterQueryType
{
QF_EQUAL,
QF_NOT_EQUAL,
QF_GREATER_THAN,
QF_GREATER_THAN_EQ,
QF_LESS_THAN,
QF_LESS_THAN_EQ,
QF_IS_EMPTY,
QF_NOT_EMPTY,
};
// Compare the cell value for a row at columnName to the cellValue using operation.
struct RAK_DLL_EXPORT FilterQuery
{
FilterQuery();
~FilterQuery();
FilterQuery(unsigned column, Cell *cell, FilterQueryType op);
// If columnName is specified, columnIndex will be looked up using it.
char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH];
unsigned columnIndex;
Cell *cellValue;
FilterQueryType operation;
};
/// Increasing or decreasing sort order
enum SortQueryType
{
QS_INCREASING_ORDER,
QS_DECREASING_ORDER,
};
// Sort on increasing or decreasing order for a particular column
struct RAK_DLL_EXPORT SortQuery
{
/// The index of the table column we are sorting on
unsigned columnIndex;
/// See SortQueryType
SortQueryType operation;
};
/// Constructor
Table();
/// Destructor
~Table();
/// \brief Adds a column to the table
/// \param[in] columnName The name of the column
/// \param[in] columnType What type of data this column will hold
/// \return The index of the new column
unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType);
/// \brief Removes a column by index
/// \param[in] columnIndex The index of the column to remove
void RemoveColumn(unsigned columnIndex);
/// \brief Gets the index of a column by name
/// Column indices are stored in the order they are added.
/// \param[in] columnName The name of the column
/// \return The index of the column, or (unsigned)-1 if no such column
unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]);
unsigned ColumnIndex(const char *columnName);
/// \brief Gives the string name of the column at a certain index
/// \param[in] index The index of the column
/// \return The name of the column, or 0 if an invalid index
char* ColumnName(unsigned index);
/// \brief Returns the type of a column, referenced by index
/// \param[in] index The index of the column
/// \return The type of the column
ColumnType GetColumnType(unsigned index);
/// Returns the number of columns
/// \return The number of columns in the table
unsigned GetColumnCount(void) const;
/// Returns the number of rows
/// \return The number of rows in the table
unsigned GetRowCount(void) const;
/// \brief Adds a row to the table
/// New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values
/// It's up to you to ensure that the values in the specific cells match the type of data used by that row
/// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys.
/// rowId must be unique
/// Rows are stored in sorted order in the table, using rowId as the sort key
/// \param[in] rowId The UNIQUE primary key for the row. This can never be changed.
/// \param[in] initialCellValues Initial values to give the row (optional)
/// \return The newly added row
Table::Row* AddRow(unsigned rowId);
Table::Row* AddRow(unsigned rowId, DataStructures::List<Cell> &initialCellValues);
/// Removes a row specified by rowId
/// \param[in] rowId The ID of the row
void RemoveRow(unsigned rowId);
/// Removes all the rows with IDs that the specified table also has
/// \param[in] tableContainingRowIDs The IDs of the rows
void RemoveRows(Table *tableContainingRowIDs);
/// Updates a particular cell in the table
/// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly.
/// \note Row pointers do not change, so you can also write directly to the rows for more efficiency.
/// \param[in] rowId The ID of the row
/// \param[in] columnIndex The column of the cell
/// \param[in] value The data to set
bool UpdateCell(unsigned rowId, unsigned columnIndex, int value);
bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str);
bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data);
bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value);
bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str);
bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data);
/// Note this is much less efficient to call than GetRow, then working with the cells directly
/// Numeric, string, binary
void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output);
void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output);
void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength);
/// Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell.
/// You can also update cells in rows from this function.
/// \param[in] rowId The ID of the row
/// \return The desired row, or 0 if no such row.
Row* GetRowByID(unsigned rowId) const;
/// Gets a row at a specific index
/// rowIndex should be less than GetRowCount()
/// \param[in] rowIndex The index of the row
/// \param[out] key The ID of the row returned
/// \return The desired row, or 0 if no such row.
Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const;
/// \brief Queries the table, optionally returning only a subset of columns and rows.
/// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns
/// \param[in] numColumnSubset The number of elements in \a columnSubset
/// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned.
/// \param[in] numInclusionFilters The number of elements in \a inclusionFilters
/// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows.
/// \param[in] numRowIDs The number of elements in \a rowIds
/// \param[out] result The result of the query. If no rows are returned, the table will only have columns.
void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result);
/// \brief Sorts the table by rows
/// You can sort the table in ascending or descending order on one or more columns
/// Columns have precedence in the order they appear in the \a sortQueries array
/// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1
/// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table
/// \param[in] numColumnSubset The number of elements in \a numSortQueries
/// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount()
void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out);
/// Frees all memory in the table.
void Clear(void);
/// Prints out the names of all the columns
/// \param[out] out A pointer to an array of bytes which will hold the output.
/// \param[in] outLength The size of the \a out array
/// \param[in] columnDelineator What character to print to delineate columns
void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const;
/// Writes a text representation of the row to \a out
/// \param[out] out A pointer to an array of bytes which will hold the output.
/// \param[in] outLength The size of the \a out array
/// \param[in] columnDelineator What character to print to delineate columns
/// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator.
/// \param[in] inputRow The row to print
void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const;
/// Direct access to make things easier
DataStructures::List<ColumnDescriptor>& GetColumns(void);
/// Direct access to make things easier
DataStructures::BPlusTree<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER>& GetRows(void);
/// Get the head of a linked list containing all the row data
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> * GetListHead(void);
/// Get the first free row id.
/// This could be made more efficient.
unsigned GetAvailableRowId(void) const;
protected:
Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List<unsigned> columnIndices);
void DeleteRow(Row *row);
void QueryRow(DataStructures::List<unsigned> &inclusionFilterColumnIndices, DataStructures::List<unsigned> &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result);
// 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for
// Insertions and deletions.
DataStructures::BPlusTree<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> rows;
// Columns in the table.
DataStructures::List<ColumnDescriptor> columns;
};
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

98
thirdparty/raknet/Source/DS_Tree.h vendored Normal file
View File

@@ -0,0 +1,98 @@
/// \file
/// \brief \b [Internal] Just a regular tree
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __DS_TREE_H
#define __DS_TREE_H
#include "Export.h"
#include "DS_List.h"
#include "DS_Queue.h"
#include "RakMemoryOverride.h"
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
template <class TreeType>
class RAK_DLL_EXPORT Tree : public RakNet::RakMemoryOverride
{
public:
Tree();
Tree(TreeType &inputData);
~Tree();
void LevelOrderTraversal(DataStructures::List<Tree*> &output);
void AddChild(TreeType &newData);
void DeleteDecendants(void);
TreeType data;
DataStructures::List<Tree *> children;
};
template <class TreeType>
Tree<TreeType>::Tree()
{
}
template <class TreeType>
Tree<TreeType>::Tree(TreeType &inputData)
{
data=inputData;
}
template <class TreeType>
Tree<TreeType>::~Tree()
{
}
template <class TreeType>
void Tree<TreeType>::LevelOrderTraversal(DataStructures::List<Tree*> &output)
{
unsigned i;
Tree<TreeType> *node;
DataStructures::Queue<Tree<TreeType>*> queue;
for (i=0; i < children.Size(); i++)
queue.Push(children[i]);
while (queue.Size())
{
node=queue.Pop();
output.Insert(node);
for (i=0; i < node->children.Size(); i++)
queue.Push(node->children[i]);
}
}
template <class TreeType>
void Tree<TreeType>::AddChild(TreeType &newData)
{
children.Insert(new Tree(newData));
}
template <class TreeType>
void Tree<TreeType>::DeleteDecendants(void)
{
DataStructures::List<Tree*> output;
LevelOrderTraversal(output);
unsigned i;
for (i=0; i < output.Size(); i++)
delete output[i];
}
}
#endif

View File

@@ -0,0 +1,544 @@
/// \file
/// \brief \b [Internal] Weighted graph. I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently).
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __WEIGHTED_GRAPH_H
#define __WEIGHTED_GRAPH_H
#include "DS_OrderedList.h"
#include "DS_Map.h"
#include "DS_Heap.h"
#include "DS_Queue.h"
#include "DS_Tree.h"
#include <assert.h>
#include "RakMemoryOverride.h"
#ifdef _DEBUG
#include <stdio.h>
#endif
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
namespace DataStructures
{
template <class node_type, class weight_type, bool allow_unlinkedNodes>
class RAK_DLL_EXPORT WeightedGraph : public RakNet::RakMemoryOverride
{
public:
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<node_type>(node_type(),node_type());}
WeightedGraph();
~WeightedGraph();
WeightedGraph( const WeightedGraph& original_copy );
WeightedGraph& operator= ( const WeightedGraph& original_copy );
void AddNode(const node_type &node);
void RemoveNode(const node_type &node);
void AddConnection(const node_type &node1, const node_type &node2, weight_type weight);
void RemoveConnection(const node_type &node1, const node_type &node2);
bool HasConnection(const node_type &node1, const node_type &node2);
void Print(void);
void Clear(void);
bool GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT);
bool GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT );
unsigned GetNodeCount(void) const;
unsigned GetConnectionCount(unsigned nodeIndex) const;
void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const;
node_type GetNodeAtIndex(unsigned nodeIndex) const;
protected:
void ClearDijkstra(void);
void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT);
DataStructures::Map<node_type, DataStructures::Map<node_type, weight_type> *> adjacencyLists;
// All these variables are for path finding with Dijkstra
// 08/23/06 Won't compile as a DLL inside this struct
// struct
// {
bool isValidPath;
node_type rootNode;
DataStructures::OrderedList<node_type, node_type> costMatrixIndices;
weight_type *costMatrix;
node_type *leastNodeArray;
// } dijkstra;
struct NodeAndParent
{
DataStructures::Tree<node_type>*node;
DataStructures::Tree<node_type>*parent;
};
};
template <class node_type, class weight_type, bool allow_unlinkedNodes>
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph()
{
isValidPath=false;
costMatrix=0;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::~WeightedGraph()
{
Clear();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph( const WeightedGraph& original_copy )
{
adjacencyLists=original_copy.adjacencyLists;
isValidPath=original_copy.isValidPath;
if (isValidPath)
{
rootNode=original_copy.rootNode;
costMatrixIndices=original_copy.costMatrixIndices;
costMatrix = new weight_type[costMatrixIndices.Size() * costMatrixIndices.Size()];
leastNodeArray = new node_type[costMatrixIndices.Size()];
memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
}
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>& WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::operator=( const WeightedGraph& original_copy )
{
adjacencyLists=original_copy.adjacencyLists;
isValidPath=original_copy.isValidPath;
if (isValidPath)
{
rootNode=original_copy.rootNode;
costMatrixIndices=original_copy.costMatrixIndices;
costMatrix = new weight_type[costMatrixIndices.Size() * costMatrixIndices.Size()];
leastNodeArray = new node_type[costMatrixIndices.Size()];
memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
}
return *this;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddNode(const node_type &node)
{
adjacencyLists.SetNew(node, new DataStructures::Map<node_type, weight_type>);
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveNode(const node_type &node)
{
unsigned i;
DataStructures::Queue<node_type> removeNodeQueue;
removeNodeQueue.Push(node);
while (removeNodeQueue.Size())
{
delete adjacencyLists.Pop(removeNodeQueue.Pop());
// Remove this node from all of the other lists as well
for (i=0; i < adjacencyLists.Size(); i++)
{
adjacencyLists[i]->Delete(node);
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
if (allow_unlinkedNodes==false && adjacencyLists[i]->Size()==0)
removeNodeQueue.Push(adjacencyLists.GetKeyAtIndex(i));
}
}
ClearDijkstra();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::HasConnection(const node_type &node1, const node_type &node2)
{
if (node1==node2)
return false;
if (adjacencyLists.Has(node1)==false)
return false;
return adjacencyLists.Get(node1)->Has(node2);
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddConnection(const node_type &node1, const node_type &node2, weight_type weight)
{
if (node1==node2)
return;
if (adjacencyLists.Has(node1)==false)
AddNode(node1);
adjacencyLists.Get(node1)->Set(node2, weight);
if (adjacencyLists.Has(node2)==false)
AddNode(node2);
adjacencyLists.Get(node2)->Set(node1, weight);
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveConnection(const node_type &node1, const node_type &node2)
{
adjacencyLists.Get(node2)->Delete(node1);
adjacencyLists.Get(node1)->Delete(node2);
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node
{
if (adjacencyLists.Get(node1)->Size()==0)
RemoveNode(node1); // Will also remove node1 from the adjacency list of node2
if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0)
RemoveNode(node2);
}
ClearDijkstra();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Clear(void)
{
unsigned i;
for (i=0; i < adjacencyLists.Size(); i++)
delete adjacencyLists[i];
adjacencyLists.Clear();
ClearDijkstra();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT)
{
path.Clear();
if (startNode==endNode)
{
path.Insert(startNode);
path.Insert(endNode);
return true;
}
if (isValidPath==false || rootNode!=startNode)
{
ClearDijkstra();
GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT);
}
// return the results
bool objectExists;
unsigned col,row;
weight_type currentWeight;
DataStructures::Queue<node_type> outputQueue;
col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists);
if (costMatrixIndices.Size()<2)
{
return false;
}
if (objectExists==false)
{
return false;
}
node_type vertex;
row=costMatrixIndices.Size()-2;
if (row==0)
{
path.Insert(startNode);
path.Insert(endNode);
return true;
}
currentWeight=costMatrix[row*adjacencyLists.Size() + col];
if (currentWeight==INFINITE_WEIGHT)
{
// No path
return true;
}
vertex=endNode;
outputQueue.PushAtHead(vertex);
row--;
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while (1)
{
while (costMatrix[row*adjacencyLists.Size() + col] == currentWeight)
{
if (row==0)
{
path.Insert(startNode);
for (col=0; outputQueue.Size(); col++)
path.Insert(outputQueue.Pop());
return true;
}
--row;
}
vertex=leastNodeArray[row];
outputQueue.PushAtHead(vertex);
if (row==0)
break;
col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists);
currentWeight=costMatrix[row*adjacencyLists.Size() + col];
}
path.Insert(startNode);
for (col=0; outputQueue.Size(); col++)
path.Insert(outputQueue.Pop());
return true;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
node_type WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeAtIndex(unsigned nodeIndex) const
{
return adjacencyLists.GetKeyAtIndex(nodeIndex);
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeCount(void) const
{
return adjacencyLists.Size();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionCount(unsigned nodeIndex) const
{
return adjacencyLists[nodeIndex]->Size();
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const
{
outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex);
outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex);
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT )
{
// Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable
DataStructures::List<node_type> path;
DataStructures::WeightedGraph<node_type, weight_type, allow_unlinkedNodes> outGraph;
bool res;
unsigned i,j;
for (i=0; i < inputNodes->Size(); i++)
{
res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT);
if (res)
{
for (j=0; j < path.Size()-1; j++)
{
// Don't bother looking up the weight
outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT);
}
}
}
// Copy the graph to a tree.
DataStructures::Queue<NodeAndParent> nodesToProcess;
DataStructures::Tree<node_type> *current;
DataStructures::Map<node_type, weight_type> *adjacencyList;
node_type key;
NodeAndParent nap, nap2;
outTree.DeleteDecendants();
outTree.data=startNode;
current=&outTree;
if (outGraph.adjacencyLists.Has(startNode)==false)
return false;
adjacencyList = outGraph.adjacencyLists.Get(startNode);
for (i=0; i < adjacencyList->Size(); i++)
{
nap2.node=new DataStructures::Tree<node_type>;
nap2.node->data=adjacencyList->GetKeyAtIndex(i);
nap2.parent=current;
nodesToProcess.Push(nap2);
current->children.Insert(nap2.node);
}
while (nodesToProcess.Size())
{
nap=nodesToProcess.Pop();
current=nap.node;
adjacencyList = outGraph.adjacencyLists.Get(nap.node->data);
for (i=0; i < adjacencyList->Size(); i++)
{
key=adjacencyList->GetKeyAtIndex(i);
if (key!=nap.parent->data)
{
nap2.node=new DataStructures::Tree<node_type>;
nap2.node->data=key;
nap2.parent=current;
nodesToProcess.Push(nap2);
current->children.Insert(nap2.node);
}
}
}
return true;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT)
{
if (adjacencyLists.Size()==0)
return;
costMatrix = new weight_type[adjacencyLists.Size() * adjacencyLists.Size()];
leastNodeArray = new node_type[adjacencyLists.Size()];
node_type currentNode;
unsigned col, row, row2, openSetIndex;
node_type adjacentKey;
unsigned adjacentIndex;
weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight;
DataStructures::Map<node_type, weight_type> *adjacencyList;
DataStructures::Heap<weight_type, node_type, false> minHeap;
DataStructures::Map<node_type, weight_type> openSet;
for (col=0; col < adjacencyLists.Size(); col++)
{
// This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck
costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true);
}
for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++)
costMatrix[col]=INFINITE_WEIGHT;
currentNode=startNode;
row=0;
currentNodeWeight=0;
rootNode=startNode;
// Clear the starting node column
if (adjacencyLists.Size())
{
adjacentIndex=adjacencyLists.GetIndexAtKey(startNode);
for (row2=0; row2 < adjacencyLists.Size(); row2++)
costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0;
}
while (row < adjacencyLists.Size()-1)
{
adjacencyList = adjacencyLists.Get(currentNode);
// Go through all connections from the current node. If the new weight is less than the current weight, then update that weight.
for (col=0; col < adjacencyList->Size(); col++)
{
edgeWeight=(*adjacencyList)[col];
adjacentKey=adjacencyList->GetKeyAtIndex(col);
adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey);
adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex];
if (currentNodeWeight + edgeWeight < adjacentNodeWeight)
{
// Update the weight for the adjacent node
for (row2=row; row2 < adjacencyLists.Size(); row2++)
costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight;
openSet.Set(adjacentKey, currentNodeWeight + edgeWeight);
}
}
// Find the lowest in the open set
minHeap.Clear();
for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++)
minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex));
/*
unsigned i,j;
for (i=0; i < adjacencyLists.Size()-1; i++)
{
for (j=0; j < adjacencyLists.Size(); j++)
{
printf("%2i ", costMatrix[i*adjacencyLists.Size() + j]);
}
printf("Node=%i", leastNodeArray[i]);
printf("\n");
}
*/
if (minHeap.Size()==0)
{
// Unreachable nodes
isValidPath=true;
return;
}
currentNodeWeight=minHeap.PeekWeight(0);
leastNodeArray[row]=minHeap.Pop(0);
currentNode=leastNodeArray[row];
openSet.Delete(currentNode);
row++;
}
/*
#ifdef _DEBUG
unsigned i,j;
for (i=0; i < adjacencyLists.Size()-1; i++)
{
for (j=0; j < adjacencyLists.Size(); j++)
{
printf("%2i ", costMatrix[i*adjacencyLists.Size() + j]);
}
printf("Node=%i", leastNodeArray[i]);
printf("\n");
}
#endif
*/
isValidPath=true;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::ClearDijkstra(void)
{
if (isValidPath)
{
isValidPath=false;
delete [] costMatrix;
delete [] leastNodeArray;
costMatrixIndices.Clear();
}
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Print(void)
{
#ifdef _DEBUG
unsigned i,j;
for (i=0; i < adjacencyLists.Size(); i++)
{
//printf("%i connected to ", i);
printf("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString());
if (adjacencyLists[i]->Size()==0)
printf("<Empty>");
else
{
for (j=0; j < adjacencyLists[i]->Size(); j++)
// printf("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) );
printf("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) );
}
printf("\n");
}
#endif
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

View File

@@ -0,0 +1,206 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "DataBlockEncryptor.h"
#include "CheckSum.h"
#include "GetTime.h"
#include "Rand.h"
#include <assert.h>
#include <string.h>
#include "Rijndael.h"
#include "Types.h"
DataBlockEncryptor::DataBlockEncryptor()
{
keySet = false;
}
DataBlockEncryptor::~DataBlockEncryptor()
{}
bool DataBlockEncryptor::IsKeySet( void ) const
{
return keySet;
}
void DataBlockEncryptor::SetKey( const unsigned char key[ 16 ] )
{
keySet = true;
//secretKeyAES128.set_key( key );
makeKey(&keyEncrypt, DIR_ENCRYPT, 16, (char*)key);
makeKey(&keyDecrypt, DIR_DECRYPT, 16, (char*)key);
cipherInit(&cipherInst, MODE_ECB, 0); // ECB is not secure except that I chain manually farther down.
}
void DataBlockEncryptor::UnsetKey( void )
{
keySet = false;
}
void DataBlockEncryptor::Encrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength )
{
unsigned index, byteIndex, lastBlock;
unsigned int checkSum;
unsigned char paddingBytes;
unsigned char encodedPad;
unsigned char randomChar;
CheckSum checkSumCalculator;
#ifdef _DEBUG
assert( keySet );
#endif
assert( input && inputLength );
// randomChar will randomize the data so the same data sent twice will not look the same
randomChar = (unsigned char) randomMT();
// 16-(((x-1) % 16)+1)
// # of padding bytes is 16 -(((input_length + extra_data -1) % 16)+1)
paddingBytes = (unsigned char) ( 16 - ( ( ( inputLength + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) - 1 ) % 16 ) + 1 ) );
// Randomize the pad size variable
encodedPad = (unsigned char) randomMT();
encodedPad <<= 4;
encodedPad |= paddingBytes;
*outputLength = inputLength + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes;
// Write the data first, in case we are overwriting ourselves
if ( input == output )
memmove( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes, input, inputLength );
else
memcpy( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes, input, inputLength );
// Write the random char
memcpy( output + sizeof( checkSum ), ( char* ) & randomChar, sizeof( randomChar ) );
// Write the pad size variable
memcpy( output + sizeof( checkSum ) + sizeof( randomChar ), ( char* ) & encodedPad, sizeof( encodedPad ) );
// Write the padding
for ( index = 0; index < paddingBytes; index++ )
*( output + sizeof( checkSum ) + sizeof( randomChar ) + sizeof( encodedPad ) + index ) = (unsigned char) randomMT();
// Calculate the checksum on the data
checkSumCalculator.Add( output + sizeof( checkSum ), inputLength + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes );
checkSum = checkSumCalculator.Get();
// Write checksum
#ifdef HOST_ENDIAN_IS_BIG
output[0] = checkSum&0xFF;
output[1] = (checkSum>>8)&0xFF;
output[2] = (checkSum>>16)&0xFF;
output[3] = (checkSum>>24)&0xFF;
#else
memcpy( output, ( char* ) & checkSum, sizeof( checkSum ) );
#endif
// AES on the first block
// secretKeyAES128.encrypt16( output );
blockEncrypt(&cipherInst, &keyEncrypt, output, 16, output);
lastBlock = 0;
// Now do AES on every other block from back to front
for ( index = *outputLength - 16; index >= 16; index -= 16 )
{
for ( byteIndex = 0; byteIndex < 16; byteIndex++ )
output[ index + byteIndex ] ^= output[ lastBlock + byteIndex ];
//secretKeyAES128.encrypt16( output + index );
blockEncrypt(&cipherInst, &keyEncrypt, output+index, 16, output+index);
lastBlock = index;
}
}
bool DataBlockEncryptor::Decrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength )
{
unsigned index, byteIndex, lastBlock;
unsigned int checkSum;
unsigned char paddingBytes;
unsigned char encodedPad;
unsigned char randomChar;
CheckSum checkSumCalculator;
#ifdef _DEBUG
assert( keySet );
#endif
if ( input == 0 || inputLength < 16 || ( inputLength % 16 ) != 0 )
{
return false;
}
// Unchain in reverse order
for ( index = 16; index <= inputLength - 16;index += 16 )
{
// secretKeyAES128.decrypt16( input + index );
blockDecrypt(&cipherInst, &keyDecrypt, input + index, 16, output + index);
for ( byteIndex = 0; byteIndex < 16; byteIndex++ )
{
if ( index + 16 == ( unsigned ) inputLength )
output[ index + byteIndex ] ^= input[ byteIndex ];
else
output[ index + byteIndex ] ^= input[ index + 16 + byteIndex ];
}
lastBlock = index;
};
// Decrypt the first block
//secretKeyAES128.decrypt16( input );
blockDecrypt(&cipherInst, &keyDecrypt, input, 16, output);
// Read checksum
#ifdef HOST_ENDIAN_IS_BIG
checkSum = (unsigned int)output[0] | (unsigned int)(output[1]<<8) |
(unsigned int)(output[2]<<16)|(unsigned int)(output[3]<<24);
#else
memcpy( ( char* ) & checkSum, output, sizeof( checkSum ) );
#endif
// Read the pad size variable
memcpy( ( char* ) & encodedPad, output + sizeof( randomChar ) + sizeof( checkSum ), sizeof( encodedPad ) );
// Ignore the high 4 bytes
paddingBytes = encodedPad & 0x0F;
// Get the data length
*outputLength = inputLength - sizeof( randomChar ) - sizeof( checkSum ) - sizeof( encodedPad ) - paddingBytes;
// Calculate the checksum on the data.
checkSumCalculator.Add( output + sizeof( checkSum ), *outputLength + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes );
if ( checkSum != checkSumCalculator.Get() )
return false;
// Read the data
//if ( input == output )
memmove( output, output + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength );
//else
// memcpy( output, input + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength );
return true;
}

View File

@@ -0,0 +1,71 @@
/// \file
/// \brief \b [Internal] Encrypts and decrypts data blocks. Used as part of secure connections.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __DATA_BLOCK_ENCRYPTOR_H
#define __DATA_BLOCK_ENCRYPTOR_H
#include "Rijndael.h"
#include "RakMemoryOverride.h"
/// Encrypts and decrypts data blocks.
class DataBlockEncryptor : public RakNet::RakMemoryOverride
{
public:
/// Constructor
DataBlockEncryptor();
/// Destructor
~DataBlockEncryptor();
/// \return true if SetKey has been called previously
bool IsKeySet( void ) const;
/// Set the encryption key
/// \param[in] key The new encryption key
void SetKey( const unsigned char key[ 16 ] );
/// Unset the encryption key
void UnsetKey( void );
/// Encryption adds 6 data bytes and then pads the number of bytes to be a multiple of 16. Output should be large enough to hold this.
/// Output can be the same memory block as input
/// \param[in] input the input buffer to encrypt
/// \param[in] inputLength the size of the @em input buffer
/// \param[in] output the output buffer to store encrypted data
/// \param[in] outputLength the size of the output buffer
void Encrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength );
/// Decryption removes bytes, as few as 6. Output should be large enough to hold this.
/// Output can be the same memory block as input
/// \param[in] input the input buffer to decrypt
/// \param[in] inputLength the size of the @em input buffer
/// \param[in] output the output buffer to store decrypted data
/// \param[in] outputLength the size of the @em output buffer
/// \return False on bad checksum or input, true on success
bool Decrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength );
protected:
keyInstance keyEncrypt;
keyInstance keyDecrypt;
cipherInstance cipherInst;
bool keySet;
};
#endif

View File

@@ -0,0 +1,58 @@
#include "DataCompressor.h"
#include "DS_HuffmanEncodingTree.h"
#include <assert.h>
#include <string.h> // Use string.h rather than memory.h for a console
void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output )
{
// Don't use this for small files as you will just make them bigger!
assert(sizeInBytes > 2048);
unsigned int frequencyTable[ 256 ];
unsigned int i;
memset(frequencyTable,0,256*sizeof(unsigned int));
for (i=0; i < sizeInBytes; i++)
++frequencyTable[userData[i]];
HuffmanEncodingTree tree;
BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2;
tree.GenerateFromFrequencyTable(frequencyTable);
output->WriteCompressed(sizeInBytes);
for (i=0; i < 256; i++)
output->WriteCompressed(frequencyTable[i]);
output->AlignWriteToByteBoundary();
writeOffset1=output->GetWriteOffset();
output->Write((unsigned int)0); // Dummy value
bitsUsed1=output->GetNumberOfBitsUsed();
tree.EncodeArray(userData, sizeInBytes, output);
bitsUsed2=output->GetNumberOfBitsUsed();
writeOffset2=output->GetWriteOffset();
output->SetWriteOffset(writeOffset1);
output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding
output->SetWriteOffset(writeOffset2);
}
unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output )
{
HuffmanEncodingTree tree;
unsigned int bitsUsed, destinationSizeInBytes, decompressedBytes;
unsigned int frequencyTable[ 256 ];
unsigned i;
input->ReadCompressed(destinationSizeInBytes);
for (i=0; i < 256; i++)
input->ReadCompressed(frequencyTable[i]);
input->AlignReadToByteBoundary();
if (input->Read(bitsUsed)==false)
{
// Read error
#ifdef _DEBUG
assert(0);
#endif
return 0;
}
*output = (unsigned char*) rakMalloc(destinationSizeInBytes);
tree.GenerateFromFrequencyTable(frequencyTable);
decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output );
assert(decompressedBytes==destinationSizeInBytes);
return destinationSizeInBytes;
}

View File

@@ -0,0 +1,34 @@
/// \file
/// \brief DataCompressor does compression on a block of data. Not very good compression, but it's small and fast so is something you can use per-message at runtime.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __DATA_COMPRESSOR_H
#define __DATA_COMPRESSOR_H
#include "RakMemoryOverride.h"
#include "DS_HuffmanEncodingTree.h"
#include "Export.h"
/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime.
class RAK_DLL_EXPORT DataCompressor : public RakNet::RakMemoryOverride
{
public:
static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output );
static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output );
};
#endif

View File

@@ -0,0 +1,216 @@
#include "DirectoryDeltaTransfer.h"
#include "FileList.h"
#include "StringCompressor.h"
#include "RakPeerInterface.h"
#include "FileListTransfer.h"
#include "FileListTransferCBInterface.h"
#include "BitStream.h"
#include "MessageIdentifiers.h"
#include "FileOperations.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
class DDTCallback : public FileListTransferCBInterface
{
public:
unsigned subdirLen;
char outputSubdir[512];
FileListTransferCBInterface *onFileCallback;
DDTCallback() {}
virtual ~DDTCallback() {}
virtual bool OnFile(OnFileStruct *onFileStruct)
{
char fullPathToDir[1024];
if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName))
{
strcpy(fullPathToDir, outputSubdir);
strcat(fullPathToDir, onFileStruct->fileName+subdirLen);
WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->finalDataLength);
}
else
fullPathToDir[0]=0;
return onFileCallback->OnFile(onFileStruct);
}
virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk)
{
char fullPathToDir[1024];
if (onFileStruct->fileName && subdirLen < strlen(onFileStruct->fileName))
{
strcpy(fullPathToDir, outputSubdir);
strcat(fullPathToDir, onFileStruct->fileName+subdirLen);
}
else
fullPathToDir[0]=0;
onFileCallback->OnFileProgress(onFileStruct, partCount, partTotal, partLength, firstDataChunk);
}
virtual bool OnDownloadComplete(void)
{
return onFileCallback->OnDownloadComplete();
}
};
DirectoryDeltaTransfer::DirectoryDeltaTransfer()
{
applicationDirectory[0]=0;
fileListTransfer=0;
availableUploads = new FileList;
rakPeer=0;
priority=HIGH_PRIORITY;
orderingChannel=0;
compressOutgoingSends=false;
}
DirectoryDeltaTransfer::~DirectoryDeltaTransfer()
{
delete availableUploads;
}
void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt)
{
fileListTransfer=flt;
if (flt)
availableUploads->SetCallback(flt->GetCallback());
else
availableUploads->SetCallback(0);
}
void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication)
{
if (pathToApplication==0 || pathToApplication[0]==0)
applicationDirectory[0]=0;
else
{
strncpy(applicationDirectory, pathToApplication, 510);
if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\')
strcat(applicationDirectory, "/");
applicationDirectory[511]=0;
}
}
void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel)
{
priority=_priority;
orderingChannel=_orderingChannel;
}
void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir)
{
availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, 0);
}
unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb)
{
if (rakPeer->IsConnected(host)==false)
return (unsigned short) -1;
DDTCallback *transferCallback;
FileList localFiles;
localFiles.SetCallback(cb);
// Get a hash of all the files that we already have (if any)
localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, 0);
// Prepare the callback data
transferCallback = new DDTCallback;
if (subdir && subdir[0])
{
transferCallback->subdirLen=(unsigned int)strlen(subdir);
if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\')
transferCallback->subdirLen++;
}
else
transferCallback->subdirLen=0;
if (prependAppDirToOutputSubdir)
strcpy(transferCallback->outputSubdir, applicationDirectory);
else
transferCallback->outputSubdir[0]=0;
if (outputSubdir)
strcat(transferCallback->outputSubdir, outputSubdir);
if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\')
strcat(transferCallback->outputSubdir, "/");
transferCallback->onFileCallback=onFileCallback;
// Setup the transfer plugin to get the response to this download request
unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host);
// Send to the host, telling it to process this request
RakNet::BitStream outBitstream;
outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST);
outBitstream.Write(setId);
stringCompressor->EncodeString(subdir, 256, &outBitstream);
stringCompressor->EncodeString(outputSubdir, 256, &outBitstream);
localFiles.Serialize(&outBitstream);
rakPeer->Send(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false);
return setId;
}
void DirectoryDeltaTransfer::ClearUploads(void)
{
availableUploads->Clear();
}
void DirectoryDeltaTransfer::OnDownloadRequest(RakPeerInterface *peer, Packet *packet)
{
(void) peer;
char subdir[256];
char remoteSubdir[256];
RakNet::BitStream inBitstream(packet->data, packet->length, false);
FileList remoteFileHash;
FileList delta;
unsigned short setId;
inBitstream.IgnoreBits(8);
inBitstream.Read(setId);
stringCompressor->DecodeString(subdir, 256, &inBitstream);
stringCompressor->DecodeString(remoteSubdir, 256, &inBitstream);
if (remoteFileHash.Deserialize(&inBitstream)==false)
{
#ifdef _DEBUG
assert(0);
#endif
return;
}
availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir);
delta.PopulateDataFromDisk(applicationDirectory, true, false, true);
// This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system
fileListTransfer->Send(&delta, rakPeer, packet->systemAddress, setId, priority, orderingChannel, compressOutgoingSends);
}
void DirectoryDeltaTransfer::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
void DirectoryDeltaTransfer::Update(RakPeerInterface *peer)
{
(void) peer;
}
PluginReceiveResult DirectoryDeltaTransfer::OnReceive(RakPeerInterface *peer, Packet *packet)
{
switch (packet->data[0])
{
case ID_DDT_DOWNLOAD_REQUEST:
OnDownloadRequest(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
return RR_CONTINUE_PROCESSING;
}
void DirectoryDeltaTransfer::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
}
unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const
{
return availableUploads->fileList.Size();
}
void DirectoryDeltaTransfer::SetCompressOutgoingSends(bool compress)
{
compressOutgoingSends=compress;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,134 @@
/// \file
/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __DIRECTORY_DELTA_TRANSFER_H
#define __DIRECTORY_DELTA_TRANSFER_H
#include "RakMemoryOverride.h"
#include "RakNetTypes.h"
#include "Export.h"
#include "PluginInterface.h"
#include "DS_Map.h"
#include "PacketPriority.h"
class RakPeerInterface;
class FileList;
struct Packet;
struct InternalPacket;
struct DownloadRequest;
class FileListTransfer;
class FileListTransferCBInterface;
class FileListProgress;
/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer
/// \ingroup PLUGINS_GROUP
/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc.
/// \sa Autopatcher class for database driven patching, including binary deltas and search by date.
///
/// To use, first set the path to your application. For example "C:/Games/MyRPG/"
/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative
/// to the path to your application. This includes subdirectories.
/// For example:
/// SetApplicationDirectory("C:/Games/MyRPG/");
/// AddUploadsFromSubdirectory("Mods/Skins/");
/// would allow downloads from
/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"
/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows
/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems.
/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP
class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface
{
public:
/// Constructor
DirectoryDeltaTransfer();
/// Destructor
virtual ~DirectoryDeltaTransfer();
/// This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files.
/// So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here.
/// \param[in] flt A pointer to a registered instance of FileListTransfer
void SetFileListTransferPlugin(FileListTransfer *flt);
/// Set the local root directory to base all file uploads and downloads off of.
/// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk.
void SetApplicationDirectory(const char *pathToApplication);
/// What parameters to use for the RakPeerInterface::Send() call when uploading files.
/// \param[in] _priority See RakPeerInterface::Send()
/// \param[in] _orderingChannel See RakPeerInterface::Send()
void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel);
/// Add all files in the specified subdirectory recursively
/// \a subdir is appended to \a pathToApplication in SetApplicationDirectory().
/// All files in the resultant directory and subdirectories are then hashed so that users can download them.
/// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin
/// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads.
void AddUploadsFromSubdirectory(const char *subdir);
/// Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory.
/// \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory
/// Therefore,
/// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"...
/// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"...
/// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin
/// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir.
/// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want.
/// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true.
/// \param[in] host The address of the remote system to send the message to.
/// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done
/// \param[in] _priority See RakPeerInterface::Send()
/// \param[in] _orderingChannel See RakPeerInterface::Send()
/// \param[in] cb Callback to get progress updates. Pass 0 to not use.
/// \return A set ID, identifying this download set. Returns 65535 on host unreachable.
unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb);
/// Clear all allowed uploads previously set with AddUploadsFromSubdirectory
void ClearUploads(void);
/// Returns how many files are available for upload
/// \return How many files are available for upload
unsigned GetNumberOfFilesForUpload(void) const;
/// Set if we should compress outgoing sends or not
/// Defaults to false, because this results in a noticeable freeze on large requests
/// You can set this to true if you only send small files though
/// \param[in] compress True to compress, false to not.
void SetCompressOutgoingSends(bool compress);
/// \internal For plugin handling
virtual void OnAttach(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void Update(RakPeerInterface *peer);
/// \internal For plugin handling
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal For plugin handling
virtual void OnShutdown(RakPeerInterface *peer);
protected:
void OnDownloadRequest(RakPeerInterface *peer, Packet *packet);
char applicationDirectory[512];
FileListTransfer *fileListTransfer;
FileList *availableUploads;
RakPeerInterface *rakPeer;
PacketPriority priority;
char orderingChannel;
bool compressOutgoingSends;
};
#endif

409
thirdparty/raknet/Source/EmailSender.cpp vendored Normal file
View File

@@ -0,0 +1,409 @@
// Useful sites
// http://www.faqs.org\rfcs\rfc2821.html
// http://en.wikipedia.org/wiki/Base64
// http://www2.rad.com\networks/1995/mime/examples.htm
#include "EmailSender.h"
#include "TCPInterface.h"
#include "GetTime.h"
#include "Rand.h"
#include "FileList.h"
#include "BitStream.h"
#include <stdio.h>
#if defined(_XBOX360)
#include "Console1Includes.h"
#endif
#include "RakSleep.h"
static const char base64Map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password)
{
Packet *packet;
char query[1024];
TCPInterface tcpInterface;
SystemAddress emailServer;
if (tcpInterface.Start(0, 0)==false)
return "Unknown error starting TCP";
emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
return "Failed to connect to host";
RakNetTime timeoutTime = RakNet::GetTime()+3000;
packet=0;
while (RakNet::GetTime() < timeoutTime)
{
packet = tcpInterface.Receive();
if (packet)
{
if (doPrintf)
printf("%s", packet->data);
break;
}
RakSleep(250);
}
if (packet==0)
return "Timeout while waiting for initial data from server.";
tcpInterface.Send("EHLO\r\n", 6, emailServer);
const char *response;
bool authenticate=false;
#ifdef _MSC_VER
#pragma warning(disable:4127) // conditional expression is constant
#endif
while (1)
{
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
{
authenticate=true;
break;
}
// Something other than continue?
if (response!=0 && strcmp(response, "CONTINUE")!=0)
return response;
// Success?
if (response==0)
break;
}
if (authenticate)
{
sprintf(query, "EHLO %s\r\n", sender);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
if (password==0)
return "Password needed";
char *outputData = new char [(strlen(sender)+strlen(password)+2)*3];
RakNet::BitStream bs;
char zero=0;
bs.Write(&zero,1);
bs.Write(sender,(const unsigned int)strlen(sender));
//bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
bs.Write(&zero,1);
bs.Write(password,(const unsigned int)strlen(password));
bs.Write(&zero,1);
//bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData, base64Map);
sprintf(query, "AUTH PLAIN %s", outputData);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
}
if (sender)
sprintf(query, "MAIL From: <%s>\r\n", sender);
else
sprintf(query, "MAIL From: <>\r\n");
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
if (recipient)
sprintf(query, "RCPT TO: <%s>\r\n", recipient);
else
sprintf(query, "RCPT TO: <>\r\n");
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
if (subject)
{
sprintf(query, "Subject: %s\r\n", subject);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
}
if (senderName)
{
sprintf(query, "From: %s\r\n", senderName);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
}
if (recipientName)
{
sprintf(query, "To: %s\r\n", recipientName);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
}
const int boundarySize=60;
char boundary[boundarySize+1];
int i,j;
if (attachedFiles && attachedFiles->fileList.Size())
{
seedMT(RakNet::GetTime());
// Random multipart message boundary
for (i=0; i < boundarySize; i++)
boundary[i]=base64Map[randomMT()%64];
boundary[boundarySize]=0;
}
sprintf(query, "MIME-version: 1.0\r\n");
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
if (attachedFiles && attachedFiles->fileList.Size())
{
sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
}
sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
// Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period.
char *newBody;
int bodyLength;
bodyLength=(int)strlen(body);
newBody = (char*) rakMalloc( bodyLength*3 );
if (bodyLength>0)
newBody[0]=body[0];
for (i=1, j=1; i < bodyLength; i++)
{
// Transform \n . \r \n into \n . . \r \n
if (i < bodyLength-2 &&
body[i-1]=='\n' &&
body[i+0]=='.' &&
body[i+1]=='\r' &&
body[i+2]=='\n')
{
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='\r';
newBody[j++]='\n';
i+=2;
}
// Transform \n . . \r \n into \n . . . \r \n
// Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
else if (i <= bodyLength-3 &&
body[i-1]=='\n' &&
body[i+0]=='.' &&
body[i+1]=='.' &&
body[i+2]=='\r' &&
body[i+3]=='\n')
{
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='\r';
newBody[j++]='\n';
i+=3;
}
// Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
else if (i < bodyLength-1 &&
body[i-1]=='\n' &&
body[i+0]=='.' &&
body[i+1]=='\n')
{
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='\r';
newBody[j++]='\n';
i+=1;
}
// Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
// In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
else if (i <= bodyLength-2 &&
body[i-1]=='\n' &&
body[i+0]=='.' &&
body[i+1]=='.' &&
body[i+2]=='\n')
{
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='.';
newBody[j++]='\r';
newBody[j++]='\n';
i+=2;
}
else
newBody[j++]=body[i];
}
newBody[j++]='\r';
newBody[j++]='\n';
tcpInterface.Send(newBody, j, emailServer);
rakFree(newBody);
int outputOffset;
// What a pain in the rear. I have to map the binary to printable characters using 6 bits per character.
if (attachedFiles && attachedFiles->fileList.Size())
{
for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
{
// Write boundary
sprintf(query, "\r\n--%s\r\n", boundary);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename, attachedFiles->fileList[i].filename);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
newBody = (char*) rakMalloc( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2 );
outputOffset=Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody, base64Map);
// Send the base64 mapped file.
tcpInterface.Send(newBody, outputOffset, emailServer);
rakFree(newBody);
}
// Write last boundary
sprintf(query, "\r\n--%s--\r\n", boundary);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
}
sprintf(query, "\r\n.\r\n");
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer);
RakSleep(30);
if (doPrintf)
{
packet = tcpInterface.Receive();
while (packet)
{
printf("%s", packet->data);
packet = tcpInterface.Receive();
}
}
tcpInterface.Stop();
return 0; // Success
}
const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
{
Packet *packet;
RakNetTime timeout;
timeout=RakNet::GetTime()+5000;
#ifdef _MSC_VER
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
#endif
while (1)
{
if (tcpInterface->HasLostConnection()==emailServer)
return "Connection to server lost.";
packet = tcpInterface->Receive();
if (packet)
{
if (doPrintf)
{
printf("%s", packet->data);
}
#if defined(OPEN_SSL_CLIENT_SUPPORT)
if (strstr((const char*)packet->data, "220"))
{
tcpInterface->StartSSLClient(packet->systemAddress);
return "AUTHENTICATE"; // OK
}
#endif
if (strstr((const char*)packet->data, "235"))
return 0; // Authentication accepted
if (strstr((const char*)packet->data, "354"))
return 0; // OK
#if defined(OPEN_SSL_CLIENT_SUPPORT)
if (strstr((const char*)packet->data, "250-STARTTLS"))
{
tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress);
return "CONTINUE";
}
#endif
if (strstr((const char*)packet->data, "250"))
return 0; // OK
if (strstr((const char*)packet->data, "550"))
return "Failed on error code 550";
if (strstr((const char*)packet->data, "553"))
return "Failed on error code 553";
}
if (RakNet::GetTime() > timeout)
return "Timed out";
RakSleep(100);
}
}
int EmailSender::Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map)
{
int outputOffset, charCount;
int write3Count;
outputOffset=0;
charCount=0;
int j;
write3Count=dataLength/3;
for (j=0; j < write3Count; j++)
{
// 6 leftmost bits from first byte, shifted to bits 7,8 are 0
outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// 4 low bits from the second byte and the two high bits from the third byte, masked to ignore bits 7,8
outputData[outputOffset++]=base64Map[((inputData[j*3+1] << 2) | (inputData[j*3+2] >> 6)) & 63]; // Third 6 bits
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// Last 6 bits from the third byte, masked to ignore bits 7,8
outputData[outputOffset++]=base64Map[inputData[j*3+2] & 63];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
}
if (dataLength % 3==1)
{
// One input byte remaining
outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// Pad with two equals
outputData[outputOffset++]='=';
outputData[outputOffset++]='=';
}
else if (dataLength % 3==2)
{
// Two input bytes remaining
// 6 leftmost bits from first byte, shifted to bits 7,8 are 0
outputData[outputOffset++]=base64Map[inputData[j*3+0] >> 2];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// Remaining 2 bits from first byte, placed in position, and 4 high bits from the second byte, masked to ignore bits 7,8
outputData[outputOffset++]=base64Map[((inputData[j*3+0] << 4) | (inputData[j*3+1] >> 4)) & 63];
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// 4 low bits from the second byte, followed by 00
outputData[outputOffset++]=base64Map[(inputData[j*3+1] << 2) & 63]; // Third 6 bits
if ((++charCount % 76)==0) {outputData[outputOffset++]='\r'; outputData[outputOffset++]='\n'; charCount=0;}
// Pad with one equal
outputData[outputOffset++]='=';
//outputData[outputOffset++]='=';
}
// Append \r\n
outputData[outputOffset++]='\r';
outputData[outputOffset++]='\n';
outputData[outputOffset]=0;
return outputOffset;
}

51
thirdparty/raknet/Source/EmailSender.h vendored Normal file
View File

@@ -0,0 +1,51 @@
/// \file
/// \brief Rudimentary class to send email from code. Don't expect anything fancy.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __EMAIL_SENDER_H
#define __EMAIL_SENDER_H
class FileList;
class TCPInterface;
#include "RakNetTypes.h"
#include "RakMemoryOverride.h"
/// \brief Rudimentary class to send email from code.
class EmailSender : public RakNet::RakMemoryOverride
{
public:
/// Sends an email
/// \param[in] hostAddress The address of the email server.
/// \param[in] hostPort The port of the email server (usually 25)
/// \param[in] sender The email address you are sending from.
/// \param[in] recipient The email address you are sending to.
/// \param[in] senderName The email address you claim to be sending from
/// \param[in] recipientName The email address you claim to be sending to
/// \param[in] subject Email subject
/// \param[in] body Email body
/// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none).
/// \param[in] doPrintf true to output SMTP info to console(for debugging?)
/// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail)
/// \return 0 on success, otherwise a string indicating the error message
const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password);
// Returns how many bytes were written
int Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map);
protected:
const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf);
};
#endif

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,35 @@
#include "FormatString.h"
#include "EpochTimeToString.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
// localtime
#include <time.h>
#include "LinuxStrings.h"
char * EpochTimeToString(long long time)
{
static int textIndex=0;
static char text[4][64];
if (++textIndex==4)
textIndex=0;
struct tm * timeinfo;
time_t t = time;
timeinfo = localtime ( &t );
strftime (text[textIndex],64,"%c.",timeinfo);
/*
time_t
// Copied from the docs
struct tm *newtime;
newtime = _localtime64(& time);
asctime_s( text[textIndex], sizeof(text[textIndex]), newtime );
while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r'))
text[textIndex][strlen(text[textIndex])-1]=0;
*/
return text[textIndex];
}

View File

@@ -0,0 +1,9 @@
#ifndef __EPOCH_TIME_TO_STRING_H
#define __EPOCH_TIME_TO_STRING_H
#include "Export.h"
RAK_DLL_EXPORT char * EpochTimeToString(long long time);
#endif

6
thirdparty/raknet/Source/Export.h vendored Normal file
View File

@@ -0,0 +1,6 @@
// Fuck it, GCC won't compile with exports. Someone else can fix this if they want
#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL)
#define RAK_DLL_EXPORT __declspec(dllexport)
#else
#define RAK_DLL_EXPORT
#endif

View File

@@ -0,0 +1,67 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
// No longer used as I no longer support IO Completion ports
/*
#ifdef __USE_IO_COMPLETION_PORTS
#include "ExtendedOverlappedPool.h"
ExtendedOverlappedPool ExtendedOverlappedPool::I;
ExtendedOverlappedPool::ExtendedOverlappedPool()
{}
ExtendedOverlappedPool::~ExtendedOverlappedPool()
{
// The caller better have returned all the packets!
ExtendedOverlappedStruct * p;
poolMutex.Lock();
while ( pool.Size() )
{
p = pool.Pop();
delete p;
}
poolMutex.Unlock();
}
ExtendedOverlappedStruct* ExtendedOverlappedPool::GetPointer( void )
{
ExtendedOverlappedStruct * p = 0;
poolMutex.Lock();
if ( pool.Size() )
p = pool.Pop();
poolMutex.Unlock();
if ( p )
return p;
return new ExtendedOverlappedStruct;
}
void ExtendedOverlappedPool::ReleasePointer( ExtendedOverlappedStruct *p )
{
poolMutex.Lock();
pool.Push( p );
poolMutex.Unlock();
}
#endif
*/

View File

@@ -0,0 +1,50 @@
/// \file
/// \brief \b [Depreciated] This was used for IO completion ports.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
// No longer used as I no longer support IO Completion ports
/*
#ifdef __USE_IO_COMPLETION_PORTS
#ifndef __EXTENDED_OVERLAPPED_POOL
#define __EXTENDED_OVERLAPPED_POOL
#include "SimpleMutex.h"
#include "ClientContextStruct.h"
#include "DS_Queue.h"
/// Depreciated - for IO completion ports
class ExtendedOverlappedPool
{
public:
ExtendedOverlappedPool();
~ExtendedOverlappedPool();
ExtendedOverlappedStruct* GetPointer( void );
void ReleasePointer( ExtendedOverlappedStruct *p );
static inline ExtendedOverlappedPool* Instance()
{
return & I;
}
private:
DataStructures::Queue<ExtendedOverlappedStruct*> pool;
SimpleMutex poolMutex;
static ExtendedOverlappedPool I;
};
#endif
#endif
*/

692
thirdparty/raknet/Source/FileList.cpp vendored Normal file
View File

@@ -0,0 +1,692 @@
#include "FileList.h"
#include <stdio.h> // printf
#include <assert.h>
#if defined(_WIN32) || defined(__CYGWIN__)
#include <io.h>
#elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ )
#include <sys/io.h>
#endif
#include "DS_Queue.h"
#ifdef _WIN32
// For mkdir
#include <direct.h>
#else
#include <sys/stat.h>
#endif
//#include "SHA1.h"
#include "StringCompressor.h"
#include "BitStream.h"
#include "FileOperations.h"
#include "SuperFastHash.h"
#include "RakAssert.h"
#include "LinuxStrings.h"
#define MAX_FILENAME_LENGTH 512
static const unsigned HASH_LENGTH=sizeof(unsigned int);
// alloca
#ifdef _XBOX360
#elif defined(_WIN32)
#include <malloc.h>
#else
#if !defined ( __FreeBSD__ )
#include <alloca.h>
#endif
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "_FindFirst.h"
#include <stdint.h> //defines intptr_t
#endif
//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data )
//{
// return strcmp(key, data.filename);
//}
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// First callback called when FileList::AddFilesFromDirectory() starts
void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) {
(void) fileList;
printf("Adding files from directory %s\n",dir);}
/// Called for each directory, when that directory begins processing
void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) {
(void) fileList;
printf("Adding %s. %i remaining.\n", dir, directoriesRemaining);}
FileList::FileList()
{
callback=0;
}
FileList::~FileList()
{
Clear();
}
void FileList::AddFile(const char *filepath, const char *filename, unsigned char context)
{
if (filepath==0 || filename==0)
return;
char *data;
//std::fstream file;
//file.open(filename, std::ios::in | std::ios::binary);
FILE *fp = fopen(filepath, "rb");
if (fp==0)
return;
fseek(fp, 0, SEEK_END);
int length = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (length > (int) ((unsigned int)-1 / 8))
{
// If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons
assert("Cannot add files over 536 MB" && 0);
fclose(fp);
return;
}
#if !defined(_XBOX360)
bool usedAlloca=false;
if (length < MAX_ALLOCA_STACK_ALLOCATION)
{
data = ( char* ) alloca( length );
usedAlloca=true;
}
else
#endif
{
data = (char*) rakMalloc( length );
}
fread(data, 1, length, fp);
AddFile(filename, data, length, length, context);
fclose(fp);
#if !defined(_XBOX360)
if (usedAlloca==false)
#endif
rakFree(data);
}
void FileList::AddFile(const char *filename, const char *data, const unsigned dataLength, const unsigned fileLength, unsigned char context)
{
if (filename==0)
return;
if (strlen(filename)>MAX_FILENAME_LENGTH)
{
// Should be enough for anyone
assert(0);
return;
}
// Avoid duplicate insertions unless the data is different, in which case overwrite the old data
unsigned i;
for (i=0; i<fileList.Size();i++)
{
if (strcmp(fileList[i].filename, filename)==0)
{
if (fileList[i].fileLengthBytes==fileLength && fileList[i].dataLengthBytes==dataLength &&
(dataLength==0 || memcmp(fileList[i].data, data, dataLength)==0))
// Exact same file already here
return;
// File of the same name, but different contents, so overwrite
rakFree(fileList[i].data);
rakFree(fileList[i].filename);
fileList.RemoveAtIndex(i);
break;
}
}
FileListNode n;
n.filename=(char*) rakMalloc( strlen(filename)+1 );
if (dataLength)
{
n.data=(char*) rakMalloc( dataLength );
memcpy(n.data, data, dataLength);
}
else
n.data=0;
n.dataLengthBytes=dataLength;
n.fileLengthBytes=fileLength;
n.context=context;
strcpy(n.filename, filename);
fileList.Insert(n);
}
void FileList::AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, unsigned char context)
{
DataStructures::Queue<char*> dirList;
char root[260];
char fullPath[520];
_finddata_t fileInfo;
intptr_t dir;
FILE *fp;
char *dirSoFar, *fileData;
dirSoFar=(char*) rakMalloc( 520 );
if (applicationDirectory)
strcpy(root, applicationDirectory);
else
root[0]=0;
int rootLen=(int)strlen(root);
if (rootLen)
{
strcpy(dirSoFar, root);
if (FixEndingSlash(dirSoFar))
rootLen++;
}
else
dirSoFar[0]=0;
if (subDirectory)
{
strcat(dirSoFar, subDirectory);
FixEndingSlash(dirSoFar);
}
if (callback)
callback->OnAddFilesFromDirectoryStarted(this, dirSoFar);
// printf("Adding files from directory %s\n",dirSoFar);
dirList.Push(dirSoFar);
while (dirList.Size())
{
dirSoFar=dirList.Pop();
strcpy(fullPath, dirSoFar);
// Changed from *.* to * for Linux compatibility
strcat(fullPath, "*");
dir=_findfirst(fullPath, &fileInfo );
if (dir==-1)
{
_findclose(dir);
rakFree(dirSoFar);
unsigned i;
for (i=0; i < dirList.Size(); i++)
rakFree(dirList[i]);
return;
}
// printf("Adding %s. %i remaining.\n", fullPath, dirList.Size());
if (callback)
callback->OnDirectory(this, fullPath, dirList.Size());
do
{
// no guarantee these entries are first...
if (strcmp("." , fileInfo.name) == 0 ||
strcmp("..", fileInfo.name) == 0)
{
continue;
}
if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0)
{
strcpy(fullPath, dirSoFar);
strcat(fullPath, fileInfo.name);
fileData=0;
if (callback)
callback->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size);
if (writeData && writeHash)
{
fileData= (char*) rakMalloc( fileInfo.size+HASH_LENGTH );
fp = fopen(fullPath, "rb");
fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp);
fclose(fp);
unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size);
memcpy(fileData, &hash, HASH_LENGTH);
// sha1.Reset();
// sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size );
// sha1.Final();
// memcpy(fileData, sha1.GetHash(), HASH_LENGTH);
// File data and hash
AddFile((const char*)fullPath+rootLen, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context);
}
else if (writeHash)
{
// sha1.Reset();
// sha1.HashFile((char*)fullPath);
// sha1.Final();
unsigned int hash = SuperFastHashFile(fullPath);
// Hash only
// AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context);
AddFile((const char*)fullPath+rootLen, (const char*)&hash, HASH_LENGTH, fileInfo.size, context);
}
else if (writeData)
{
fileData= (char*) rakMalloc( fileInfo.size );
fp = fopen(fullPath, "rb");
fread(fileData, fileInfo.size, 1, fp);
fclose(fp);
// File data only
AddFile(fullPath+rootLen, fileData, fileInfo.size, fileInfo.size, context);
}
else
{
// Just the filename
AddFile(fullPath+rootLen, 0, 0, fileInfo.size, context);
}
if (fileData)
rakFree(fileData);
}
else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive)
{
char *newDir=(char*) rakMalloc( 520 );
strcpy(newDir, dirSoFar);
strcat(newDir, fileInfo.name);
strcat(newDir, "/");
dirList.Push(newDir);
}
} while (_findnext(dir, &fileInfo ) != -1);
_findclose(dir);
rakFree(dirSoFar);
}
}
void FileList::Clear(void)
{
unsigned i;
for (i=0; i<fileList.Size(); i++)
{
rakFree(fileList[i].data);
rakFree(fileList[i].filename);
}
fileList.Clear();
}
void FileList::Serialize(RakNet::BitStream *outBitStream)
{
outBitStream->WriteCompressed(fileList.Size());
unsigned i;
for (i=0; i < fileList.Size(); i++)
{
outBitStream->WriteCompressed(fileList[i].context);
stringCompressor->EncodeString(fileList[i].filename, MAX_FILENAME_LENGTH, outBitStream);
outBitStream->Write((bool)(fileList[i].dataLengthBytes>0==true));
if (fileList[i].dataLengthBytes>0)
{
outBitStream->WriteCompressed(fileList[i].dataLengthBytes);
outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes);
}
outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes));
if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes)
outBitStream->WriteCompressed(fileList[i].fileLengthBytes);
}
}
bool FileList::Deserialize(RakNet::BitStream *inBitStream)
{
bool b, dataLenNonZero=false, fileLenMatchesDataLen=false;
char filename[512];
unsigned int fileListSize;
FileListNode n;
b=inBitStream->ReadCompressed(fileListSize);
#ifdef _DEBUG
assert(b);
assert(fileListSize < 10000);
#endif
if (b==false || fileListSize > 10000)
return false; // Sanity check
Clear();
unsigned i;
for (i=0; i < fileListSize; i++)
{
inBitStream->ReadCompressed(n.context);
stringCompressor->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream);
inBitStream->Read(dataLenNonZero);
if (dataLenNonZero)
{
inBitStream->ReadCompressed(n.dataLengthBytes);
// sanity check
if (n.dataLengthBytes>2000000000)
{
#ifdef _DEBUG
assert(n.dataLengthBytes<=2000000000);
#endif
return false;
}
n.data=(char*) rakMalloc( (size_t) n.dataLengthBytes );
inBitStream->Read(n.data, n.dataLengthBytes);
}
else
{
n.dataLengthBytes=0;
n.data=0;
}
b=inBitStream->Read(fileLenMatchesDataLen);
if (fileLenMatchesDataLen)
n.fileLengthBytes=(unsigned) n.dataLengthBytes;
else
b=inBitStream->ReadCompressed(n.fileLengthBytes);
#ifdef _DEBUG
assert(b);
#endif
if (b==0)
{
Clear();
return false;
}
n.filename=(char*) rakMalloc( strlen(filename)+1 );
strcpy(n.filename, filename);
fileList.Insert(n);
}
return true;
}
void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir)
{
// For all files in this list that do not match the input list, write them to the output list.
// dirSubset allows checking only a portion of the files in this list.
unsigned thisIndex, inputIndex;
unsigned dirSubsetLen, localPathLen, remoteSubdirLen;
bool match;
if (dirSubset)
dirSubsetLen = (unsigned int) strlen(dirSubset);
else
dirSubsetLen = 0;
if (remoteSubdir && remoteSubdir[0])
{
remoteSubdirLen=(unsigned int) strlen(remoteSubdir);
if (IsSlash(remoteSubdir[remoteSubdirLen-1]))
remoteSubdirLen--;
}
else
remoteSubdirLen=0;
for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++)
{
localPathLen = (unsigned int) strlen(fileList[thisIndex].filename);
while (localPathLen>0)
{
if (IsSlash(fileList[thisIndex].filename[localPathLen-1]))
{
localPathLen--;
break;
}
localPathLen--;
}
// fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length.
if (dirSubsetLen>0 &&
(localPathLen<dirSubsetLen ||
_strnicmp(fileList[thisIndex].filename, dirSubset, dirSubsetLen)!=0 ||
(localPathLen>dirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false)))
continue;
match=false;
for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++)
{
// If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output
if (_stricmp(input->fileList[inputIndex].filename+remoteSubdirLen,fileList[thisIndex].filename+dirSubsetLen)==0)
{
match=true;
if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes &&
input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes &&
memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0)
{
// File exists on both machines and is the same.
break;
}
else
{
// File exists on both machines and is not the same.
output->AddFile(fileList[thisIndex].filename, 0,0, fileList[thisIndex].fileLengthBytes, 0);
break;
}
}
}
if (match==false)
{
// Other system does not have the file at all
output->AddFile(fileList[thisIndex].filename, 0,0, fileList[thisIndex].fileLengthBytes, 0);
}
}
}
void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash)
{
unsigned fileLength;
// CSHA1 sha1;
FILE *fp;
char fullPath[512];
unsigned i;
// char *fileData;
for (i=0; i < fileList.Size(); i++)
{
strcpy(fullPath, applicationDirectory);
FixEndingSlash(fullPath);
strcat(fullPath,fileList[i].filename);
fp=fopen(fullPath, "rb");
if (fp==0)
{
missingOrChangedFiles->AddFile(fileList[i].filename, 0, 0, 0, 0);
}
else
{
fseek(fp, 0, SEEK_END);
fileLength = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false)
{
missingOrChangedFiles->AddFile(fileList[i].filename, 0, 0, fileLength, 0);
}
else
{
// fileData= (char*) rakMalloc( fileLength );
// fread(fileData, fileLength, 1, fp);
// sha1.Reset();
// sha1.Update( ( unsigned char* ) fileData, fileLength );
// sha1.Final();
// rakFree(fileData);
unsigned int hash = SuperFastHashFilePtr(fp);
//if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0)
if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0)
{
if (neverWriteHash==false)
// missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0);
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char *) &hash, HASH_LENGTH, fileLength, 0);
else
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, 0, 0, fileLength, 0);
}
}
fclose(fp);
}
}
}
void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles)
{
FILE *fp;
char fullPath[512];
unsigned i;
// CSHA1 sha1;
i=0;
while (i < fileList.Size())
{
rakFree(fileList[i].data);
strcpy(fullPath, applicationDirectory);
FixEndingSlash(fullPath);
strcat(fullPath,fileList[i].filename);
fp=fopen(fullPath, "rb");
if (fp)
{
if (writeFileHash || writeFileData)
{
fseek(fp, 0, SEEK_END);
fileList[i].fileLengthBytes = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (writeFileHash)
{
if (writeFileData)
{
// Hash + data so offset the data by HASH_LENGTH
fileList[i].data=(char*) rakMalloc( fileList[i].fileLengthBytes+HASH_LENGTH );
fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp);
// sha1.Reset();
// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength);
// sha1.Final();
unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes);
// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
memcpy(fileList[i].data, &hash, HASH_LENGTH);
}
else
{
// Hash only
fileList[i].dataLengthBytes=HASH_LENGTH;
if (fileList[i].fileLengthBytes < HASH_LENGTH)
fileList[i].data=(char*) rakMalloc( HASH_LENGTH );
else
fileList[i].data=(char*) rakMalloc( fileList[i].fileLengthBytes );
fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
// sha1.Reset();
// sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength);
// sha1.Final();
unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes);
// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
memcpy(fileList[i].data, &hash, HASH_LENGTH);
}
}
else
{
// Data only
fileList[i].dataLengthBytes=fileList[i].fileLengthBytes;
fileList[i].data=(char*) rakMalloc( fileList[i].fileLengthBytes );
fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
}
fclose(fp);
i++;
}
else
{
fileList[i].data=0;
fileList[i].dataLengthBytes=0;
}
}
else
{
if (removeUnknownFiles)
{
rakFree(fileList[i].filename);
fileList.RemoveAtIndex(i);
}
else
i++;
}
}
}
void FileList::WriteDataToDisk(const char *applicationDirectory)
{
char fullPath[512];
unsigned i,j;
for (i=0; i < fileList.Size(); i++)
{
strcpy(fullPath, applicationDirectory);
FixEndingSlash(fullPath);
strcat(fullPath,fileList[i].filename);
// Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory
for (j=1; j < strlen(fileList[i].filename); j++)
{
if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
{
#ifdef _DEBUG
assert(0);
#endif
// Just cancel the write entirely
return;
}
}
WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes);
}
}
#ifdef _MSC_VER
#pragma warning( disable : 4966 ) // unlink declared depreciated by Microsoft in order to make it harder to be cross platform. I don't agree it's depreciated.
#endif
void FileList::DeleteFiles(const char *applicationDirectory)
{
char fullPath[512];
unsigned i,j;
for (i=0; i < fileList.Size(); i++)
{
// The filename should not have .. in the path - if it does ignore it
for (j=1; j < strlen(fileList[i].filename); j++)
{
if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
{
#ifdef _DEBUG
assert(0);
#endif
// Just cancel the deletion entirely
return;
}
}
strcpy(fullPath, applicationDirectory);
FixEndingSlash(fullPath);
strcat(fullPath, fileList[i].filename);
#ifdef _MSC_VER
#pragma warning( disable : 4966 ) // unlink declared depreciated by Microsoft in order to make it harder to be cross platform. I don't agree it's depreciated.
#endif
int result = unlink(fullPath);
if (result!=0)
{
printf("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath);
}
}
}
void FileList::SetCallback(FileListProgress *cb)
{
callback=cb;
}
bool FileList::FixEndingSlash(char *str)
{
#ifdef _WIN32
if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\')
{
strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient
return true;
}
#else
if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/')
{
strcat(str, "/"); // Only / works with Linux
return true;
}
#endif
return false;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

171
thirdparty/raknet/Source/FileList.h vendored Normal file
View File

@@ -0,0 +1,171 @@
#ifndef __FILE_LIST
#define __FILE_LIST
#include "Export.h"
#include "DS_List.h"
#include "RakMemoryOverride.h"
#include "RakNetTypes.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
namespace RakNet
{
class BitStream;
}
/// Represents once instance of a file
struct FileListNode : public RakNet::RakMemoryOverride
{
/// Name of the file
char *filename;
/// File data (may be null if not ready)
char *data;
/// Length of \a data. May be greater than fileLength if prepended with a file hash
BitSize_t dataLengthBytes;
/// Length of the file
unsigned fileLengthBytes;
/// User specific data for whatever, describing this file.
unsigned char context;
};
//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data );
class RakPeerInterface;
class FileList;
/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called
class RAK_DLL_EXPORT FileListProgress
{
public:
FileListProgress() {}
virtual ~FileListProgress() {}
/// First callback called when FileList::AddFilesFromDirectory() starts
virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) {
(void) fileList;
(void) dir;
}
/// Called for each directory, when that directory begins processing
virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) {
(void) fileList;
(void) dir;
(void) directoriesRemaining;
}
/// Called for each file, when that file begins processing
virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) {
(void) fileList;
(void) dir;
(void) fileName;
(void) fileSize;
}
};
/// Implementation of FileListProgress to use printf
class RAK_DLL_EXPORT FLP_Printf : public FileListProgress
{
public:
FLP_Printf() {}
virtual ~FLP_Printf() {}
/// First callback called when FileList::AddFilesFromDirectory() starts
virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir);
/// Called for each directory, when that directory begins processing
virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining);
};
class RAK_DLL_EXPORT FileList : public RakNet::RakMemoryOverride
{
public:
FileList();
~FileList();
/// Add all the files at a given directory.
/// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator.
/// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename
/// \param[in] writeHash The first SHA1_LENGTH bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true)
/// \param[in] writeData Write the contents of each file
/// \param[in] recursive Whether or not to visit subdirectories
/// \param[in] context User defined byte to store with each file. Use for whatever you want.
void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, unsigned char context);
/// Deallocate all memory
void Clear(void);
/// Write all encoded data into a bitstream
void Serialize(RakNet::BitStream *outBitStream);
/// Read all encoded data from a bitstream. Clear() is called before deserializing.
bool Deserialize(RakNet::BitStream *inBitStream);
/// Given the existing set of files, search applicationDirectory for the same files.
/// For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true
/// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway.
/// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator.
/// \param[out] missingOrChangedFiles Output list written to
/// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename.
/// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison.
void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash);
/// Return the files that need to be written to make \a input match this current FileList.
/// Specify dirSubset to only consider files that start with this path
/// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames.
/// \param[in] input Full list of files
/// \param[out] output Files that we need to match input
/// \param[in] dirSubset If the filename does not start with this path, just skip this file.
/// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames.
void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir);
/// Assuming FileList contains a list of filenames presumably without data, read the data for these filenames
/// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator.
/// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true
/// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true
/// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list?
void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles);
/// Write all files to disk, prefixing the paths with applicationDirectory
/// \param[in] applicationDirectory path prefix
void WriteDataToDisk(const char *applicationDirectory);
/// Add a file, given data already in memory
/// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator.
/// \param[in] data Contents to write
/// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash
/// \param[in] fileLength Length of the file
/// \param[in] context User defined byte to store with each file. Use for whatever you want.
void AddFile(const char *filename, const char *data, const unsigned dataLength, const unsigned fileLength, unsigned char context);
/// Add a file, reading it from disk
/// \param[in] filepath Complete path to the file, including the filename itself
/// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path.
/// \param[in] context User defined byte to store with each file. Use for whatever you want.
void AddFile(const char *filepath, const char *filename, unsigned char context);
/// Delete all files stored in the file list
/// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator.
void DeleteFiles(const char *applicationDirectory);
/// Set a callback to get progress reports about what this class does
/// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid.
void SetCallback(FileListProgress *cb);
// Here so you can read it, but don't modify it
DataStructures::List<FileListNode> fileList;
static bool FixEndingSlash(char *str);
protected:
FileListProgress *callback;
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

View File

@@ -0,0 +1,561 @@
#include "FileListTransfer.h"
#include "DS_HuffmanEncodingTree.h"
#include "FileListTransferCBInterface.h"
#include "StringCompressor.h"
#include "FileList.h"
#include "DS_Queue.h"
#include "MessageIdentifiers.h"
#include "RakNetTypes.h"
#include "RakPeerInterface.h"
#include <assert.h>
#ifdef _PS3
#include <alloca.h>
#endif
#ifdef _MSC_VER
#pragma warning( push )
#endif
struct FileListTransfer::FileListReceiver
{
FileListTransferCBInterface *downloadHandler;
SystemAddress allowedSender;
HuffmanEncodingTree tree;
unsigned short setID;
unsigned setCount;
unsigned setTotalCompressedTransmissionLength;
unsigned setTotalFinalLength;
bool gotSetHeader;
bool deleteDownloadHandler;
bool isCompressed;
};
FileListTransfer::FileListTransfer()
{
rakPeer=0;
setId=0;
callback=0;
DataStructures::Map<unsigned short, FileListReceiver*>::IMPLEMENT_DEFAULT_COMPARISON();
}
FileListTransfer::~FileListTransfer()
{
Clear();
}
unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender)
{
if (rakPeer->IsConnected(allowedSender)==false)
return (unsigned short)-1;
FileListReceiver *receiver;
if (fileListReceivers.Has(setId))
{
receiver=fileListReceivers.Get(setId);
receiver->downloadHandler->OnDereference();
if (receiver->deleteDownloadHandler)
delete receiver->downloadHandler;
delete receiver;
fileListReceivers.Delete(setId);
}
unsigned short oldId;
receiver = new FileListReceiver;
receiver->downloadHandler=handler;
receiver->allowedSender=allowedSender;
receiver->gotSetHeader=false;
receiver->deleteDownloadHandler=deleteHandler;
fileListReceivers.Set(setId, receiver);
oldId=setId;
if (++setId==(unsigned short)-1)
setId=0;
return oldId;
}
void FileListTransfer::Send(FileList *fileList, RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData)
{
RakNet::BitStream outBitstream, encodedData;
HuffmanEncodingTree tree;
unsigned int frequencyTable[ 256 ];
unsigned int i,j;
unsigned totalCompressedLength, totalLength;
DataStructures::Queue<FileListNode> compressedFiles;
FileListNode n;
if (callback)
fileList->SetCallback(callback);
totalCompressedLength=totalLength=0;
if (compressData)
{
memset(frequencyTable,0,256*sizeof(unsigned int));
for (i=0; i < fileList->fileList.Size(); i++)
{
const FileListNode &fileListNode = fileList->fileList[i];
for (j=0; j < fileListNode.dataLengthBytes; j++)
{
++frequencyTable[(unsigned char)(fileListNode.data[j])];
}
}
tree.GenerateFromFrequencyTable(frequencyTable);
// Compress all the files, so we know the total compressed size to be sent
for (i=0; i < fileList->fileList.Size(); i++)
{
encodedData.Reset();
// Why send compressed chunks if we are not sending the whole file?
assert(fileList->fileList[i].fileLengthBytes==fileList->fileList[i].fileLengthBytes);
tree.EncodeArray((unsigned char*)fileList->fileList[i].data, (size_t) fileList->fileList[i].dataLengthBytes, &encodedData);
n.dataLengthBytes=encodedData.GetNumberOfBitsUsed(); // Temporarily actually storing bits
totalCompressedLength+=(unsigned int) BITS_TO_BYTES(n.dataLengthBytes);
totalLength+=fileList->fileList[i].fileLengthBytes;
n.data = (char*) rakMalloc( (size_t) BITS_TO_BYTES(n.dataLengthBytes) );
memcpy(n.data, encodedData.GetData(), (size_t) BITS_TO_BYTES(n.dataLengthBytes));
compressedFiles.Push(n);
}
}
else
{
for (i=0; i < fileList->fileList.Size(); i++)
{
const FileListNode &fileListNode = fileList->fileList[i];
totalLength+=fileListNode.fileLengthBytes;
}
}
// Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes
bool anythingToWrite;
outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_HEADER);
outBitstream.Write(setID);
anythingToWrite=fileList->fileList.Size()>0;
outBitstream.Write(anythingToWrite);
if (anythingToWrite)
{
if (compressData)
{
outBitstream.Write(true);
for (i=0; i < 256; i++)
outBitstream.WriteCompressed(frequencyTable[i]);
outBitstream.WriteCompressed(fileList->fileList.Size()); // SetCount
outBitstream.WriteCompressed(totalLength);
outBitstream.WriteCompressed(totalCompressedLength);
}
else
{
outBitstream.Write(false);
outBitstream.WriteCompressed(fileList->fileList.Size());
outBitstream.WriteCompressed(totalLength);
}
rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
// Next part arrives at FileListTransfer::DecodeFile
if (compressData)
{
// Send each possibly compressed file
for (i=0; i < compressedFiles.Size(); i++)
{
outBitstream.Reset();
outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE);
outBitstream.Write(fileList->fileList[i].context);
outBitstream.Write(setID);
outBitstream.WriteCompressed(i);
outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length
outBitstream.WriteCompressed(compressedFiles[i].dataLengthBytes); // Compressed bitlength (yes, not bytes)
stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream);
outBitstream.WriteBits((const unsigned char*)compressedFiles[i].data, compressedFiles[i].dataLengthBytes);
// rakFree(compressedFiles[i].data);
// rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
// This is much more efficient than the commented code, because it doesn't require writing the file to the bitstream just to pass to Send
char *dataBlocks[2];
int lengths[2];
dataBlocks[0]=(char*) outBitstream.GetData();
lengths[0]=outBitstream.GetNumberOfBytesUsed();
dataBlocks[1]=compressedFiles[i].data;
lengths[1]=BITS_TO_BYTES(compressedFiles[i].dataLengthBytes);
rakPeer->SendList(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
rakFree(compressedFiles[i].data);
}
}
else
{
for (i=0; i < fileList->fileList.Size(); i++)
{
outBitstream.Reset();
outBitstream.Write((MessageID)ID_FILE_LIST_TRANSFER_FILE);
outBitstream.Write(fileList->fileList[i].context);
outBitstream.Write(setID);
outBitstream.WriteCompressed(i);
outBitstream.WriteCompressed(fileList->fileList[i].dataLengthBytes); // Original length in bytes
stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream);
outBitstream.AlignWriteToByteBoundary();
// outBitstream.Write((const char*) fileList->fileList[i].data, fileList->fileList[i].dataLengthBytes);
// rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
// This is much more efficient than the commented code, because it doesn't require writing the file to the bitstream just to pass to Send
char *dataBlocks[2];
int lengths[2];
dataBlocks[0]=(char*) outBitstream.GetData();
lengths[0]=outBitstream.GetNumberOfBytesUsed();
dataBlocks[1]=fileList->fileList[i].data;
lengths[1]=fileList->fileList[i].dataLengthBytes;
rakPeer->SendList(dataBlocks,lengths,2,priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
}
}
}
else
rakPeer->Send(&outBitstream, priority, RELIABLE_ORDERED, orderingChannel, recipient, false);
}
bool FileListTransfer::DecodeSetHeader(Packet *packet)
{
unsigned i;
unsigned int frequencyTable[ 256 ];
bool anythingToWrite=false;
unsigned short setID;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
inBitStream.IgnoreBits(8);
inBitStream.Read(setID);
FileListReceiver *fileListReceiver;
if (fileListReceivers.Has(setID)==false)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
fileListReceiver=fileListReceivers.Get(setID);
if (fileListReceiver->allowedSender!=packet->systemAddress)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
#ifdef _DEBUG
assert(fileListReceiver->gotSetHeader==false);
#endif
inBitStream.Read(anythingToWrite);
if (anythingToWrite)
{
inBitStream.Read(fileListReceiver->isCompressed);
if (fileListReceiver->isCompressed)
{
for (i=0; i < 256; i++)
inBitStream.ReadCompressed(frequencyTable[i]);
fileListReceiver->tree.GenerateFromFrequencyTable(frequencyTable);
inBitStream.ReadCompressed(fileListReceiver->setCount);
inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength);
if (inBitStream.ReadCompressed(fileListReceiver->setTotalCompressedTransmissionLength))
{
fileListReceiver->gotSetHeader=true;
return true;
}
}
else
{
inBitStream.ReadCompressed(fileListReceiver->setCount);
if (inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength))
{
fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength;
fileListReceiver->gotSetHeader=true;
return true;
}
}
}
else
{
FileListTransferCBInterface::OnFileStruct s;
memset(&s,0,sizeof(FileListTransferCBInterface::OnFileStruct));
s.setID=setID;
fileListReceiver->downloadHandler->OnFile(&s);
return true;
}
return false;
}
bool FileListTransfer::DecodeFile(Packet *packet, bool fullFile)
{
FileListTransferCBInterface::OnFileStruct onFileStruct;
BitSize_t bitLength=0;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
inBitStream.IgnoreBits(8);
unsigned int partCount=0;
unsigned int partTotal=0;
unsigned int partLength=0;
onFileStruct.fileData=0;
if (fullFile==false)
{
// Disable endian swapping on reading this, as it's generated locally in ReliabilityLayer.cpp
inBitStream.ReadBits( (unsigned char* ) &partCount, BYTES_TO_BITS(sizeof(partCount)), true );
inBitStream.ReadBits( (unsigned char* ) &partTotal, BYTES_TO_BITS(sizeof(partTotal)), true );
inBitStream.ReadBits( (unsigned char* ) &partLength, BYTES_TO_BITS(sizeof(partLength)), true );
inBitStream.IgnoreBits(8);
// The header is appended to every chunk, which we continue to read after this statement block
}
inBitStream.Read(onFileStruct.context);
inBitStream.Read(onFileStruct.setID);
FileListReceiver *fileListReceiver;
if (fileListReceivers.Has(onFileStruct.setID)==false)
{
return false;
}
fileListReceiver=fileListReceivers.Get(onFileStruct.setID);
if (fileListReceiver->allowedSender!=packet->systemAddress)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
#ifdef _DEBUG
assert(fileListReceiver->gotSetHeader==true);
#endif
inBitStream.ReadCompressed(onFileStruct.fileIndex);
inBitStream.ReadCompressed(onFileStruct.finalDataLength);
if (fileListReceiver->isCompressed)
{
inBitStream.ReadCompressed(bitLength);
onFileStruct.compressedTransmissionLength=(unsigned int) BITS_TO_BYTES(bitLength);
}
else
{
// Read header uncompressed so the data is byte aligned, for speed
onFileStruct.compressedTransmissionLength=(unsigned int) onFileStruct.finalDataLength;
}
if (stringCompressor->DecodeString(onFileStruct.fileName, 512, &inBitStream)==false)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
if (fullFile)
{
// Support SendLists
inBitStream.AlignReadToByteBoundary();
onFileStruct.fileData = (char*) rakMalloc( (size_t) onFileStruct.finalDataLength );
if (fileListReceiver->isCompressed)
{
BitSize_t len=fileListReceiver->tree.DecodeArray(&inBitStream, bitLength, (size_t) onFileStruct.finalDataLength, (unsigned char*) onFileStruct.fileData);
if (len!=onFileStruct.finalDataLength)
{
#ifdef _DEBUG
assert(0);
#endif
rakFree(onFileStruct.fileData);
return false;
}
}
else
{
inBitStream.Read((char*)onFileStruct.fileData, onFileStruct.finalDataLength);
}
}
onFileStruct.setCount=fileListReceiver->setCount;
onFileStruct.setTotalCompressedTransmissionLength=fileListReceiver->setTotalCompressedTransmissionLength;
onFileStruct.setTotalFinalLength=fileListReceiver->setTotalFinalLength;
// User callback for this file.
if (fullFile)
{
if (fileListReceiver->downloadHandler->OnFile(&onFileStruct))
rakFree(onFileStruct.fileData);
// If this set is done, free the memory for it.
if (fileListReceiver->setCount==onFileStruct.fileIndex+1)
{
if (fileListReceiver->downloadHandler->OnDownloadComplete()==false)
{
fileListReceiver->downloadHandler->OnDereference();
if (fileListReceiver->deleteDownloadHandler)
delete fileListReceiver->downloadHandler;
fileListReceivers.Delete(onFileStruct.setID);
delete fileListReceiver;
}
}
}
else
{
inBitStream.AlignReadToByteBoundary();
bool usedAlloca=false;
char *firstDataChunk;
unsigned int unreadBits = inBitStream.GetNumberOfUnreadBits();
unsigned int unreadBytes = BITS_TO_BYTES(unreadBits);
if (unreadBytes>0)
{
#if !defined(_XBOX360)
if (unreadBits < MAX_ALLOCA_STACK_ALLOCATION)
{
firstDataChunk = ( char* ) alloca( unreadBytes );
usedAlloca=true;
}
else
#endif
firstDataChunk = (char*) rakMalloc( unreadBytes );
// Read partLength bytes, reported to OnFileProgress
if (fileListReceiver->isCompressed)
fileListReceiver->tree.DecodeArray(&inBitStream, unreadBits, partLength, (unsigned char*) firstDataChunk);
else
inBitStream.Read((char*)firstDataChunk, unreadBytes );
}
else
firstDataChunk=0;
fileListReceiver->downloadHandler->OnFileProgress(&onFileStruct, partCount, partTotal, unreadBytes, firstDataChunk);
if (usedAlloca==false)
delete [] firstDataChunk;
}
return true;
}
PluginReceiveResult FileListTransfer::OnReceive(RakPeerInterface *peer, Packet *packet)
{
(void) peer;
switch (packet->data[0])
{
case ID_CONNECTION_LOST:
case ID_DISCONNECTION_NOTIFICATION:
RemoveReceiver(packet->systemAddress);
break;
case ID_FILE_LIST_TRANSFER_HEADER:
DecodeSetHeader(packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_FILE_LIST_TRANSFER_FILE:
DecodeFile(packet, true);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_DOWNLOAD_PROGRESS:
if (packet->length>sizeof(MessageID)+sizeof(unsigned int)*3 &&
packet->data[sizeof(MessageID)+sizeof(unsigned int)*3]==ID_FILE_LIST_TRANSFER_FILE)
{
DecodeFile(packet, false);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
break;
}
return RR_CONTINUE_PROCESSING;
}
void FileListTransfer::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
Clear();
}
void FileListTransfer::Clear(void)
{
unsigned i;
for (i=0; i < fileListReceivers.Size(); i++)
{
fileListReceivers[i]->downloadHandler->OnDereference();
if (fileListReceivers[i]->deleteDownloadHandler)
{
delete fileListReceivers[i]->downloadHandler;
}
delete fileListReceivers[i];
}
fileListReceivers.Clear();
}
void FileListTransfer::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
{
(void) peer;
RemoveReceiver(systemAddress);
}
void FileListTransfer::CancelReceive(unsigned short setId)
{
if (fileListReceivers.Has(setId)==false)
{
#ifdef _DEBUG
assert(0);
#endif
return;
}
FileListReceiver *fileListReceiver=fileListReceivers.Get(setId);
fileListReceiver->downloadHandler->OnDereference();
if (fileListReceiver->deleteDownloadHandler)
delete fileListReceiver->downloadHandler;
delete fileListReceiver;
fileListReceivers.Delete(setId);
}
void FileListTransfer::RemoveReceiver(SystemAddress systemAddress)
{
unsigned i;
i=0;
while (i < fileListReceivers.Size())
{
if (fileListReceivers[i]->allowedSender==systemAddress)
{
fileListReceivers[i]->downloadHandler->OnDereference();
if (fileListReceivers[i]->deleteDownloadHandler)
{
delete fileListReceivers[i]->downloadHandler;
}
delete fileListReceivers[i];
fileListReceivers.RemoveAtIndex(i);
}
else
i++;
}
}
bool FileListTransfer::IsHandlerActive(unsigned short setId)
{
return fileListReceivers.Has(setId);
}
void FileListTransfer::SetCallback(FileListProgress *cb)
{
callback=cb;
}
FileListProgress *FileListTransfer::GetCallback(void) const
{
return callback;
}
void FileListTransfer::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
void FileListTransfer::Update(RakPeerInterface *peer)
{
(void) peer;
unsigned i;
i=0;
while (i < fileListReceivers.Size())
{
if (fileListReceivers[i]->downloadHandler->Update()==false)
{
fileListReceivers[i]->downloadHandler->OnDereference();
if (fileListReceivers[i]->deleteDownloadHandler)
delete fileListReceivers[i]->downloadHandler;
delete fileListReceivers[i];
fileListReceivers.RemoveAtIndex(i);
}
else
i++;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,110 @@
/// \file
/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __FILE_LIST_TRANFER_H
#define __FILE_LIST_TRANFER_H
#include "RakNetTypes.h"
#include "Export.h"
#include "PluginInterface.h"
#include "DS_Map.h"
#include "RakNetTypes.h"
#include "PacketPriority.h"
#include "RakMemoryOverride.h"
class FileListTransferCBInterface;
class FileList;
class FileListProgress;
/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer
/// \ingroup PLUGINS_GROUP
/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure.
/// Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk.
///
/// Usage:
/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive()
/// is the setID that is allowed.
/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get.
/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive()
/// as the \a setID parameter to FileListTransfer::Send()
/// \ingroup FILE_LIST_TRANSFER_GROUP
class RAK_DLL_EXPORT FileListTransfer : public PluginInterface
{
public:
FileListTransfer();
virtual ~FileListTransfer();
/// Allows one corresponding Send() call from another system to arrive.
/// \param[in] handler The class to call on each file
/// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so.
/// \param[in] allowedSender Which system to allow files from
/// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender)
unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender);
/// Send the FileList structure to another system, which must have previously called SetupReceive()
/// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set
/// \param[in] rakPeer The instance of RakNet to use to send the message
/// \param[in] recipient The address of the system to send to
/// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient
/// \param[in] priority Passed to RakPeerInterface::Send()
/// \param[in] orderingChannel Passed to RakPeerInterface::Send()
/// \param[in] compressData Use a poor but fast compression algorithm. This makes your data larger if it is already compressed or if the amount of data to send is small so don't use it blindly.
void Send(FileList *fileList, RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData);
/// Stop a download.
void CancelReceive(unsigned short setId);
/// Remove all handlers associated with a particular system address
void RemoveReceiver(SystemAddress systemAddress);
/// Is a handler passed to SetupReceive still running?
bool IsHandlerActive(unsigned short setId);
/// Set a callback to get progress reports about what the file list instances do
/// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid.
void SetCallback(FileListProgress *cb);
/// \Returns what was sent to SetCallback
/// \return What was sent to SetCallback
FileListProgress *GetCallback(void) const;
/// \internal For plugin handling
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal For plugin handling
virtual void OnShutdown(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress);
/// \internal For plugin handling
virtual void OnAttach(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void Update(RakPeerInterface *peer);
protected:
bool DecodeSetHeader(Packet *packet);
bool DecodeFile(Packet *packet, bool fullFile);
void Clear(void);
struct FileListReceiver;
DataStructures::Map<unsigned short, FileListReceiver*> fileListReceivers;
unsigned short setId;
RakPeerInterface *rakPeer;
FileListProgress *callback;
};
#endif

View File

@@ -0,0 +1,98 @@
#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
#include "RakMemoryOverride.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
/// \brief Used by FileListTransfer plugin as a callback for when we get a file.
/// You get the last file when fileIndex==setCount
/// \sa FileListTransfer
class FileListTransferCBInterface : public RakNet::RakMemoryOverride
{
public:
struct OnFileStruct
{
/// The index into the set of files, from 0 to setCount
unsigned fileIndex;
/// The name of the file
char fileName[512];
/// The data pointed to by the file
char *fileData;
/// How many bytes this file took to send, if using compression.
unsigned compressedTransmissionLength;
/// The actual length of this file.
BitSize_t finalDataLength;
/// Files are transmitted in sets, where more than one set of files can be transmitted at the same time.
/// This is the identifier for the set, which is returned by FileListTransfer::SetupReceive
unsigned short setID;
/// The number of files that are in this set.
unsigned setCount;
/// The total length of the transmitted files for this set, with compression.
unsigned setTotalCompressedTransmissionLength;
/// The total length of the transmitted files for this set, after being uncompressed
unsigned setTotalFinalLength;
/// User data passed to one of the functions in the FileList class.
/// However, on error, this is instead changed to one of the enumerations in the PatchContext structure.
unsigned char context;
};
FileListTransferCBInterface() {}
virtual ~FileListTransferCBInterface() {}
/// Got a file
/// This structure is only valid for the duration of this function call.
/// \return Return true to have RakNet delete the memory allocated to hold this file for this function call.
virtual bool OnFile(OnFileStruct *onFileStruct)=0;
/// Got part of a big file.
/// You can get these notifications by calling RakPeer::SetSplitMessageProgressInterval
/// Otherwise you will only get complete files.
/// \param[in] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally.
/// \param[in] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal
/// \param[in] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers
/// \param[in] partLength How many bytes long firstDataChunk is
/// \param[in] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0
virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk) {
(void) onFileStruct;
(void) partCount;
(void) partTotal;
(void) partLength;
(void) firstDataChunk;
}
/// Called while the handler is active by FileListTransfer
/// Return false when you are done with the class
/// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin.
virtual bool Update(void) {return true;}
/// Called when the download is completed.
/// If you are finished with this class, return false
/// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin.
/// Otherwise return true, and Update will continue to be called.
virtual bool OnDownloadComplete(void) {return false;}
/// This function is called when this instance is about to be dereferenced by the FileListTransfer plugin.
/// Update will no longer be called.
/// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler
/// Otherwise it is up to you to delete it yourself.
virtual void OnDereference(void) {}
};
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

View File

@@ -0,0 +1,161 @@
#include "RakMemoryOverride.h"
#include "_FindFirst.h" // For linux
#include "FileOperations.h"
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
// For mkdir
#include <direct.h>
#include <io.h>
#else
#include <sys/stat.h>
#include <unistd.h>
#include "_FindFirst.h"
#endif
#include "errno.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
#ifdef _MSC_VER
#pragma warning( disable : 4966 ) // mkdir declared depreciated by Microsoft in order to make it harder to be cross platform. I don't agree it's depreciated.
#endif
bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength )
{
int index;
FILE *fp;
char *pathCopy;
int res;
if ( path == 0 || path[ 0 ] == 0 )
return false;
pathCopy = (char*) rakMalloc( strlen( path ) + 1 );
strcpy( pathCopy, path );
// Ignore first / if there is one
if (pathCopy[0])
{
index = 1;
while ( pathCopy[ index ] )
{
if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\')
{
pathCopy[ index ] = 0;
#ifdef _WIN32
#pragma warning( disable : 4966 ) // mkdir declared depreciated by Microsoft in order to make it harder to be cross platform. I don't agree it's depreciated.
res = mkdir( pathCopy );
#else
res = mkdir( pathCopy, 0744 );
#endif
if (res<0 && errno!=EEXIST)
{
rakFree(pathCopy);
return false;
}
pathCopy[ index ] = '/';
}
index++;
}
}
if (data)
{
fp = fopen( path, "wb" );
if ( fp == 0 )
{
rakFree(pathCopy);
return false;
}
fwrite( data, 1, dataLength, fp );
fclose( fp );
}
else
{
#ifdef _WIN32
#pragma warning( disable : 4966 ) // mkdir declared depreciated by Microsoft in order to make it harder to be cross platform. I don't agree it's depreciated.
res = mkdir( pathCopy );
#else
res = mkdir( pathCopy, 0744 );
#endif
if (res<0 && errno!=EEXIST)
{
rakFree(pathCopy);
return false;
}
}
rakFree(pathCopy);
return true;
}
bool IsSlash(unsigned char c)
{
return c=='/' || c=='\\';
}
void AddSlash( char *input )
{
if (input==0 || input[0]==0)
return;
int lastCharIndex=(int) strlen(input)-1;
if (input[lastCharIndex]=='\\')
input[lastCharIndex]='/';
else if (input[lastCharIndex]!='/')
{
input[lastCharIndex+1]='/';
input[lastCharIndex+2]=0;
}
}
bool DirectoryExists(const char *directory)
{
_finddata_t fileInfo;
intptr_t dir;
char baseDirWithStars[560];
strcpy(baseDirWithStars, directory);
AddSlash(baseDirWithStars);
strcat(baseDirWithStars, "*.*");
dir=_findfirst(baseDirWithStars, &fileInfo );
if (dir==-1)
return false;
_findclose(dir);
return true;
}
void QuoteIfSpaces(char *str)
{
unsigned i;
bool hasSpace=false;
for (i=0; str[i]; i++)
{
if (str[i]==' ')
{
hasSpace=true;
break;
}
}
if (hasSpace)
{
int len=(int)strlen(str);
memmove(str+1, str, len);
str[0]='\"';
str[len]='\"';
str[len+1]=0;
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,12 @@
#ifndef __FILE_OPERATIONS_H
#define __FILE_OPERATIONS_H
#include "Export.h"
bool RAK_DLL_EXPORT WriteFileWithDirectories( const char *path, char *data, unsigned dataLength );
bool RAK_DLL_EXPORT IsSlash(unsigned char c);
void RAK_DLL_EXPORT AddSlash( char *input );
void RAK_DLL_EXPORT QuoteIfSpaces(char *str);
bool RAK_DLL_EXPORT DirectoryExists(const char *directory);
#endif

View File

@@ -0,0 +1,20 @@
#include "FormatString.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "LinuxStrings.h"
char * FormatString(const char *format, ...)
{
static int textIndex=0;
static char text[4][8096];
va_list ap;
va_start(ap, format);
if (++textIndex==4)
textIndex=0;
_vsnprintf(text[textIndex], 8096, format, ap);
va_end(ap);
text[textIndex][8096-1]=0;
return text[textIndex];
}

12
thirdparty/raknet/Source/FormatString.h vendored Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __FORMAT_STRING_H
#define __FORMAT_STRING_H
#include "Export.h"
extern "C" {
char * FormatString(const char *format, ...);
}
#endif

View File

@@ -0,0 +1,114 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "FullyConnectedMesh.h"
#include "RakPeerInterface.h"
#include "MessageIdentifiers.h"
#include "BitStream.h"
#include "ConnectionGraph.h"
#include "NatPunchthrough.h"
#include <string.h>
#include <assert.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
FullyConnectedMesh::FullyConnectedMesh()
{
pw=0;
natPunchthrough=0;
}
FullyConnectedMesh::~FullyConnectedMesh()
{
if (pw)
rakFree(pw);
}
void FullyConnectedMesh::Startup(const char *password, int _passwordLength)
{
if (pw)
rakFree(pw);
if (password)
{
pw = (char*) rakMalloc( _passwordLength );
memcpy(pw, password, _passwordLength);
passwordLength=_passwordLength;
}
else
pw=0;
}
void FullyConnectedMesh::ConnectWithNatPunchthrough(NatPunchthrough *np, SystemAddress _facilitator)
{
natPunchthrough=np;
facilitator=_facilitator;
}
void FullyConnectedMesh::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
}
void FullyConnectedMesh::Update(RakPeerInterface *peer)
{
(void) peer;
}
PluginReceiveResult FullyConnectedMesh::OnReceive(RakPeerInterface *peer, Packet *packet)
{
assert(packet);
assert(peer);
switch (packet->data[0])
{
case ID_REMOTE_NEW_INCOMING_CONNECTION: // This comes from the connection graph plugin
{
RakNet::BitStream b(packet->data, packet->length, false);
b.IgnoreBits(8);
ConnectionGraphGroupID group1, group2;
SystemAddress node1, node2;
b.Read(node1);
b.Read(group1);
if (peer->IsConnected(node1,true)==false)
{
if (natPunchthrough)
natPunchthrough->Connect(node1, pw, pw ? passwordLength : 0, facilitator);
else
peer->Connect(node1.ToString(false), node1.port, pw, pw ? passwordLength : 0);
}
b.Read(node2);
b.Read(group2);
if (peer->IsConnected(node2,true)==false)
{
if (natPunchthrough)
natPunchthrough->Connect(node2, pw, pw ? passwordLength : 0, facilitator);
else
peer->Connect(node2.ToString(false), node2.port, pw, pw ? passwordLength : 0);
}
break;
}
}
return RR_CONTINUE_PROCESSING;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,65 @@
/// \file
/// \brief Fully connected mesh plugin. This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __FULLY_CONNECTED_MESH_H
#define __FULLY_CONNECTED_MESH_H
class RakPeerInterface;
class NatPunchthrough;
#include "PluginInterface.h"
#include "RakMemoryOverride.h"
/// \defgroup FULLY_CONNECTED_MESH_GROUP FullyConnectedMesh
/// \ingroup PLUGINS_GROUP
/// Fully connected mesh plugin. This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
/// \pre You must also install the ConnectionGraph plugin. If you want a password, set it there.
/// \ingroup FULLY_CONNECTED_MESH_GROUP
class FullyConnectedMesh : public PluginInterface
{
public:
FullyConnectedMesh();
virtual ~FullyConnectedMesh();
// --------------------------------------------------------------------------------------------
// User functions
// --------------------------------------------------------------------------------------------
/// Set the password to use to connect to the other systems
void Startup(const char *password, int _passwordLength);
/// Use the NAT punchthrough system to connect rather than calling directly
/// \param[in] np Pointer to an attached instance of the NatPunchthrough plugin
/// \param[in] _facilitator Address of the NAT punchthrough facilitator
void ConnectWithNatPunchthrough(NatPunchthrough *np, SystemAddress _facilitator);
// --------------------------------------------------------------------------------------------
// Packet handling functions
// --------------------------------------------------------------------------------------------
virtual void OnShutdown(RakPeerInterface *peer);
virtual void Update(RakPeerInterface *peer);
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
protected:
char *pw;
int passwordLength;
NatPunchthrough *natPunchthrough;
SystemAddress facilitator;
};
#endif

View File

@@ -0,0 +1,147 @@
#include "FunctionThread.h"
#include "RakSleep.h"
using namespace RakNet;
#ifdef _MSC_VER
#pragma warning( push )
#endif
FunctionThread::FunctorAndContext WorkerThreadFunc(FunctionThread::FunctorAndContext input, bool *returnOutput, void* perThreadData)
{
(void) perThreadData;
FunctionThread::FunctorAndContext output;
input.functor->Process(input.context);
output.functor=input.functor;
output.context=input.context;
*returnOutput=true;
return output;
}
FunctionThread::FunctionThread()
{
pr=0;
}
FunctionThread::~FunctionThread()
{
StopThreads(false);
}
void FunctionThread::StartThreads(int numThreads)
{
threadPool.StartThreads(numThreads, 0);
}
void FunctionThread::StopThreads(bool blockOnCurrentProcessing)
{
// This ensures all waiting data is ultimately passed to a callback, so there are no leaks
CancelInput();
while (blockOnCurrentProcessing && threadPool.IsWorking())
{
CallResultHandlers();
RakSleep(30);
}
threadPool.StopThreads();
}
void FunctionThread::Push(Functor *functor, void *context)
{
FunctorAndContext input;
input.functor=functor;
input.context=context;
threadPool.AddInput(WorkerThreadFunc, input);
}
void FunctionThread::CallResultHandlers(void)
{
FunctorAndContext functorAndResult;
while (threadPool.HasOutputFast() && threadPool.HasOutput())
{
functorAndResult = threadPool.GetOutput();
functorAndResult.functor->HandleResult(false, functorAndResult.context);
if (pr) pr(functorAndResult);
}
}
void FunctionThread::CancelFunctorsWithContext(bool (*cancelThisFunctor)(FunctionThread::FunctorAndContext func, void *userData), void *userData)
{
FunctorAndContext functorAndResult;
unsigned i;
threadPool.LockInput();
for (i=0; i < threadPool.InputSize(); i++)
{
functorAndResult = threadPool.GetInputAtIndex(i);
if (cancelThisFunctor(functorAndResult, userData))
{
functorAndResult.functor->HandleResult(true, functorAndResult.context);
if (pr) pr(functorAndResult);
}
}
threadPool.ClearInput();
threadPool.UnlockInput();
}
void FunctionThread::SetPostResultFunction(void (*postResult)(FunctionThread::FunctorAndContext func))
{
pr=postResult;
}
void FunctionThread::CancelInput(void)
{
// We do it this way so that the callbacks get called so user-allocated data can be freed.
FunctorAndContext functorAndResult;
unsigned i;
threadPool.LockInput();
for (i=0; i < threadPool.InputSize(); i++)
{
functorAndResult = threadPool.GetInputAtIndex(i);
functorAndResult.functor->HandleResult(true, functorAndResult.context);
if (pr) pr(functorAndResult);
}
threadPool.ClearInput();
threadPool.UnlockInput();
}
FunctionThreadDependentClass::FunctionThreadDependentClass()
{
functionThreadWasAllocated=false;
functionThread=0;
}
FunctionThreadDependentClass::~FunctionThreadDependentClass()
{
if (functionThreadWasAllocated)
delete functionThread;
}
void FunctionThreadDependentClass::AssignFunctionThread(FunctionThread *ft)
{
if (functionThread && functionThreadWasAllocated)
{
functionThread->StopThreads(true);
delete functionThread;
}
functionThread=ft;
functionThreadWasAllocated=false;
}
void FunctionThreadDependentClass::StartFunctionThread(void)
{
if (functionThread==0)
{
functionThread = new FunctionThread;
functionThreadWasAllocated=true;
}
functionThread->StartThreads(1);
}
FunctionThread *FunctionThreadDependentClass::GetFunctionThread(void) const
{
return functionThread;
}
bool FunctionThreadDependentClass::GetFunctionThreadWasAllocated(void) const
{
return functionThreadWasAllocated;
}
void FunctionThreadDependentClass::PushFunctor(Functor *functor, void *context)
{
StartFunctionThread();
functionThread->Push(functor, context);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,130 @@
/// \file
/// \brief A set of classes to make it easier to perform asynchronous function processing.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __FUNCTION_THREAD_H
#define __FUNCTION_THREAD_H
#include "SingleProducerConsumer.h"
#include "ThreadPool.h"
#include "RakMemoryOverride.h"
#include "Export.h"
namespace RakNet
{
// Forward declarations
class Functor;
/// FunctionThread takes a stream of classes that implement a processing function, processes them in a thread, and calls a callback with the result.
/// It's a useful way to call blocking functions that you do not want to block, such as file writes and database operations.
class RAK_DLL_EXPORT FunctionThread : public RakNet::RakMemoryOverride
{
public:
FunctionThread();
~FunctionThread();
struct FunctorAndContext
{
Functor *functor;
void *context;
};
/// Starts the thread up.
void StartThreads(int numThreads);
/// Stop processing. Will also call FunctorResultHandler callbacks with /a wasCancelled set to true.
/// \param[in] blockOnCurrentProcessing Wait for the current processing to finish?
void StopThreads(bool blockOnCurrentProcessing);
/// Add a functor to the incoming stream of functors
/// \note functor MUST be a valid pointer until Functor::HandleResult() is called, at which point the pointer is returned to you.
/// \note For practical purposes this means the instance of functor you pass to this function has to be allocated using new and delete.
/// \note You should deallocate the pointer inside Functor::HandleResult()
/// \param[in] functor A pointer to an implemented Functor class
/// \param[in] If there is some context to this functor you want to look up to cancel it, you can set it here. Returned back to you in Functor::HandleResult
void Push(Functor *functor, void *context=0);
/// Call FunctorResultHandler callbacks
/// Normally you would call this once per update cycle, although you do not have to.
void CallResultHandlers(void);
/// If you want to cancel input and output functors associated with some context, you can pass a function to do that here
/// \param[in] cancelThisFunctor Function should return true to cancel the functor, false to let it process
/// \param[in] userData Pointer to whatever you want. Passed to the cancelThisFunctor call
void CancelFunctorsWithContext(bool (*cancelThisFunctor)(FunctorAndContext func, void *userData), void *userData);
/// If you want to automatically do some kind of processing on every functor after Functor::HandleResult is called, set it here.
/// Useful to cleanup FunctionThread::Push::context
/// \param[in] postResult pointer to a C function to do post-processing
void SetPostResultFunction(void (*postResult)(FunctorAndContext func));
protected:
void CancelInput(void);
ThreadPool<FunctorAndContext, FunctorAndContext> threadPool;
void (*pr)(FunctorAndContext func);
};
/// A functor is a single unit of processing to send to the Function thread.
/// Derive from it, add your data, and implement the processing function.
class Functor : public RakNet::RakMemoryOverride
{
public:
Functor() {}
virtual ~Functor() {}
/// Do whatever processing you want.
/// \param[in] context pointer passed to FunctionThread::Push::context
virtual void Process(void *context)=0;
/// Called from FunctionThread::CallResultHandlers with wasCancelled false OR
/// Called from FunctionThread::StopThread or FunctionThread::~FunctionThread with wasCancelled true
/// \param[in] wasCancelledTrue if CallResultHandlers was called, false if StopThreads or CancelInputWithContext was called before Functor::Process()
/// \param[in] context pointer passed to FunctionThread::Push::context
virtual void HandleResult(bool wasCancelled, void *context)=0;
};
class RAK_DLL_EXPORT FunctionThreadDependentClass : public RakNet::RakMemoryOverride
{
public:
FunctionThreadDependentClass();
virtual ~FunctionThreadDependentClass();
/// Assigns a function thread to process asynchronous calls. If you do not assign one then one will be created automatically.
/// \param[in] ft An instance of a running function thread class. This class can be shared and used for other functors as well.
virtual void AssignFunctionThread(FunctionThread *ft);
/// \return Returns the function thread held in the class
FunctionThread *GetFunctionThread(void) const;
/// \returns Whether or not this class allocated the function thread by itself
bool GetFunctionThreadWasAllocated(void) const;
/// Allocates and starts the thread if needed, and pushes the functor
/// \param[in] functor Functor to push
/// \param[in] context Sent to FunctionThread::Push::context
virtual void PushFunctor(Functor *functor, void *context=0);
protected:
/// Allocates and starts the function thread, if necessary
void StartFunctionThread();
FunctionThread *functionThread;
bool functionThreadWasAllocated;
};
}
#endif

759
thirdparty/raknet/Source/Gen_RPC8.cpp vendored Normal file
View File

@@ -0,0 +1,759 @@
#include "Gen_RPC8.h"
unsigned int GenRPC::BuildStack(char *stack)
{
char *stackPtr = (char*) stack;
SerializeHeader(stackPtr, 0);
return (unsigned int)(stackPtr-stack);
}
/// \internal
void GenRPC::Push( char*& p, char*const i, bool doEndianSwap) {
(void) doEndianSwap;
size_t len = strlen( i ) + 1;
memcpy( (void*)p, i, len );
p += len;
}
/// \internal
void GenRPC::Push( char*& p, const char*const i, bool doEndianSwap ) {
(void) doEndianSwap;
size_t len = strlen( i ) + 1;
memcpy( (void*)p, i, len );
p += len;
}
/// \internal
unsigned GenRPC::D_type( const char*const ) { return STR_PARAM; }
/// \internal
unsigned GenRPC::D_type( char*const ) { return STR_PARAM; }
/// \internal
unsigned GenRPC::D_type( float ) { return REAL_PARAM; }
/// \internal
unsigned GenRPC::D_type( double ) { return REAL_PARAM; }
/// \internal
unsigned GenRPC::D_type( long double ) { return REAL_PARAM; }
/// \internal
size_t GenRPC::D_size( char*const str ) { return strlen( str ) + 1; }
/// \internal
size_t GenRPC::D_size( char const *const str ){ return strlen( str ) + 1; }
void GenRPC::SerializeHeader(char *&out, unsigned int numParams)
{
*out = (char) numParams;
out++;
//out[*writeOffset]=(char) numParams;
//*writeOffset+=sizeof(unsigned char);
}
//
// @params
// call: [IN/OUT] workspace to build parameters that we will pass to function
// in: [IN/OUT] is the serialized buffer - used as a temporary working for swapping
// parameters.
// inLength: [IN] is the length of the above
// lastParam: [IN] final parameter, added onto the list
// thisPtr: [IN] if not zero - the value of this (added to start of list).
//
// @returns:
// true: parameter list created successfully.
// false: if deserialization fails for some reason.
//
bool GenRPC::DeserializeParametersAndBuildCall(
CallParams &call,
char *in, unsigned int inLength,
void *lastParam, void *thisPtr
) {
#if AUTO_RPC_ABI
NaturalWord *intCallParam = call.intParams;
char *refParam = call.refParams;
#if AUTO_RPC_ALLOC_SEPARATE_FLOATS
HardwareReal *realCallParam = call.realParams;
#endif
#if AUTO_RPC_CREATE_FLOAT_MAP
call.realMap = 0;
call.numRealParams = 0;
#endif
#if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64
// large structure parameters have to be bumped up here - which corresponds with the start
// of the parameters that *are* passed on the stack.
NaturalWord *memParam = &call.intParams[ AUTO_RPC_INT_REG_PARAMS ];
#endif
// this is first arg - assume space ;-)
#pragma warning(disable:4311) // pointer truncation
if ( thisPtr )
*(intCallParam++) = reinterpret_cast<NaturalWord>( thisPtr );
unsigned int serializedArgs = *(unsigned char*)in;
unsigned char* header = (unsigned char*)in + 1;
unsigned char* data = &header[ serializedArgs * ( sizeof( unsigned int ) + 1 ) ];
// check we have the entire header in buffer
if ( data > (unsigned char*) &in[ inLength ] )
return 0;
for ( unsigned int i = 0; i < serializedArgs; i++ )
{
// read heade entry
int const plen = *(unsigned int*)header;
header += sizeof( unsigned int );
unsigned char const flags = *( header++ );
// Some bits are not used by the current implementation. So if we find them we bail because
// we clearly are not equipped to handle the data - and is there no safe way to "fail".
if ( flags & RESERVED_BITS )
return 0;
#ifndef __BITSTREAM_NATIVE_END
if (flags & DO_ENDIAN_SWAP)
{
RakNet::BitStream::ReverseBytesInPlace( (unsigned char*)&plen , sizeof( plen ) );
}
#endif
if ( !plen || data + plen > (unsigned char*)&in[ inLength ] )
return 0;
// handle null-terminated strings.
if ( ( flags & PARAM_TYPE_MASK ) == STR_PARAM )
{
if ( intCallParam + 1 >= AUTO_RPC_ARRAY_END( call.intParams ) )
return 0;
// Check this has some sort of null termination. NB this assumes string is genuine Ascii
// or UTF+8; using unicode (ie. UTF+16, UCS) could break this.
if ( data[ plen - 1 ] != 0 )
return 0;
// The string doesn't need to be aligned, so we leave it in place; saving a copy, and
// preventing clogging up of our buffers with data.
#pragma warning(disable:4311) // pointer truncation
*( intCallParam++ ) = reinterpret_cast<NaturalWord>( data );
data += plen;
continue;
}
#ifndef __BITSTREAM_NATIVE_END
if (flags & DO_ENDIAN_SWAP)
{
RakNet::BitStream::ReverseBytesInPlace( (unsigned char*)data , plen );
}
#endif
// convert pointer to ref.
if ( ( flags & PARAM_TYPE_MASK ) == REF_PARAM
#if AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD
|| plen > AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD
#endif
)
{
char *nextRefParam = refParam + AUTO_RPC__ALIGN_P2( plen, AUTO_RPC_REF_ALIGN );
if ( nextRefParam >= AUTO_RPC_ARRAY_END( call.refParams ) || intCallParam + 1 >= AUTO_RPC_ARRAY_END( call.intParams ) )
return 0;
memcpy( refParam, data, plen );
#pragma warning(disable:4311) // pointer truncation
*( intCallParam++ ) = reinterpret_cast<NaturalWord>( refParam );
refParam = nextRefParam;
data += plen;
continue;
}
// Guarantee we have space on the output stack to accommodate the parameter.
NaturalWord *nextParam = (NaturalWord*)( (char*)intCallParam + AUTO_RPC_ALIGN_P2( plen, NaturalWord ) );
if ( nextParam >= AUTO_RPC_ARRAY_END( call.intParams ) )
return 0;
#if AUTO_RPC_ALLOC_SEPARATE_FLOATS
if ( ( flags & PARAM_TYPE_MASK ) == REAL_PARAM
// long doubles, of various sizes (10, 16), all get passed on the stack
&& (size_t) plen <= sizeof(double)
// once we've allocated all our floats, they get treated as ordinary int params
&& realCallParam < AUTO_RPC_ARRAY_END( call.realParams )
)
{
if ( plen != sizeof( float ) && plen != sizeof( double ) ) {
printf("illegal float size %d\n", plen );
// We can't handle it - it's not a real real :lol:
return 0;
}
#ifdef __BIG_ENDIAN__
memcpy( (char*)( realCallParam + 1 ) - plen, data, plen );
#else
memcpy( (char*)realCallParam, data, plen );
#endif
#if !AUTO_RPC_INT_SHADOW_OF_FLOATS
// we didn't use the int slot, so don't allow an advance.
nextParam = intCallParam;
#endif
// next time, we use the next Real slot
realCallParam++;
}
#if !AUTO_RPC_INT_SHADOW_OF_FLOATS
else
#endif
#endif // AUTO_RPC_ALLOC_SEPARATE_FLOATS
{
// the processor can atomically zero-extend small types, so even with the test,
// it should be faster than memcpy+memset.
if ( plen == 1 )
*intCallParam = *(uint8_t*)data; // should resolve to movzx and a move
else if ( plen == 2 )
*intCallParam = *(uint16_t*)data; // if network order replace use htons(), and skip EndianSwap()
else if ( plen == 4 )
*intCallParam = *(uint32_t*)data; // if network order replace use htonl(), and skip EndianSwap()
#if AUTO_RPC_AUTORPC_WORD == 64
else if ( plen == 8 )
*intCallParam = *(uint64_t*)data;
#endif
#if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64
//
// SYSV ABI: aggregates greater 16 bytes must go on the stack;
// in practice, that means they can't come below AUTO_RPC_INT_REG_PARAMS when we call a function.
//
else if ( plen > 16 || ( plen > 8 && intCallParam == &call.intParams[ AUTO_RPC_INT_REG_PARAMS - 1] ) || ( flags & REAL_PARAM ) )
{
if ( intCallParam < memParam )
{
NaturalWord*const nextMemParam = (NaturalWord*)( (char*)memParam + AUTO_RPC_ALIGN_P2( plen, NaturalWord ) );
if ( nextMemParam >= AUTO_RPC_ARRAY_END( call.intParams ) )
return 0;
memcpy( memParam, data, plen );
// prevent advancing the ptr slot, since we didn't use it.
nextParam = intCallParam;
// but advance the memparam
memParam = nextMemParam;
}
else
{
memcpy( (void*)intCallParam, data, plen );
}
}
#endif // AUTO_RPC_ABI_SYSV_AMD64
else
{
// We don't need to worry about alignment, because any type that's not a whole multiple
// of the natual word size will be an aggregate and that should be at the base of memory -
// this is true for some PowerPC systems (see [e]) but not all. But hey, you
// probably should be passing such structs by reference.
//
// Zeroing is also unecessary as code shouldn't be reading beyodn the bounds of the structure.
//
memcpy( (void*)intCallParam, data, plen );
}
}
#if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64
// skip over any stored "class MEMORY" (see [b]) parameters.
if ( nextParam == &call.intParams[AUTO_RPC_INT_REG_PARAMS] )
intCallParam = memParam;
else
#endif
// advance to next output param
intCallParam = nextParam;
#if !AUTO_RPC_ALLOC_SEPARATE_FLOATS && AUTO_RPC_CREATE_FLOAT_MAP
if ( ( flags & PARAM_TYPE_MASK ) == REAL_PARAM && i < AUTO_RPC_FLOAT_REG_PARAMS && ( plen == sizeof( double ) || plen == sizeof( float ) ) )
{
call.numRealParams++;
call.realMap |= ( 1 << i );
}
#endif
// advance to next input arg.
data += plen;
}
// space for lastParam?
if ( &intCallParam[1] >= AUTO_RPC_ARRAY_END( call.intParams ) )
return 0;
#pragma warning(disable:4311) // pointer truncation
*( intCallParam++ ) = reinterpret_cast<NaturalWord >( lastParam );
#if AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64
// figure out how many args we have notched up.
if ( memParam > &call.intParams[AUTO_RPC_INT_REG_PARAMS] && memParam > intCallParam )
intCallParam = memParam;
#endif
// convert from ptrdif_t to unsigned int; should be small enough, even if its 64-bit pointers.
call.numIntParams = ( unsigned int )( intCallParam - call.intParams );
#if AUTO_RPC_FLOAT_REG_PARAMS && AUTO_RPC_ALLOC_SEPARATE_FLOATS
call.numRealParams = ( unsigned int )( realCallParam - call.realParams );
#endif
return 1;
#else // AUTO_RPC_ABI
return 0;
#endif
}
//
// @params
// callParams: [IN] parameter list
// functionPtr: [IN] function to call.
//
// @returns:
// true: function was called.
// false: too many parameters, probably.
//
bool GenRPC::CallWithStack( CallParams& call, void *functionPtr ) {
#if AUTO_RPC_ABI
// Are we x86-32?
#if !defined( AUTO_RPC_NO_ASM ) && ( defined(__i386__) || defined( _M_IX86 ) || defined( __INTEL__ ) )
#if !defined(__GNUC__)
// Intel dialect assembly
NaturalWord const paramc = call.numIntParams;
#pragma warning(disable:4311) // pointer truncation
NaturalWord const paramv = reinterpret_cast<NaturalWord>( call.intParams );
_asm
{
// Load numbytes.
mov ecx, paramc
// allocate space on the stack for all these params
lea edi,dword ptr[ecx * 4]
sub esp,edi
// setup source of copy
mov esi, paramv
// Setup the destination of the copy: the return stack.
mov edi,esp
// copy data
rep movs dword ptr es:[edi],dword ptr [esi]
// call the function
call functionPtr
// Restore the stack to its state, prior to our invocation.
//
// Detail: edi is one of the registers that must be preserved
// across function calls. (The compiler should be saving it for us.)
//
// We left edi pointing to the end of the block copied; i.e. the state
// of the stack prior to copying our params. So by loading it
// into the esp we can restore the return stack to the state prior
// to the copy.
//
mov esp,edi
};
#else
// GCC has its own form of asm block - so we'll always have to write two versions.
// Therefore, as we're writing it twice, we use the ATT dialect, because only later
// gcc support Intel dialect. This one also aligns the stack to a multiple of 16 bytes; which
// windows doesn't seem to care about.
// Be aware, we can't use offset of to get the address, as gcc insists on sticking.
// NaturalWord const paramv = reinterpret_cast<NaturalWord>( call.intParams );
asm (\
"lea 4(%%ecx),%%esi\n\
mov (%%ecx),%%ecx\n\
lea (,%%ecx,4),%%edi\n\
sub %%edi,%%esp\n\
mov $12,%%edx\n\
and %%esp,%%edx\n\
sub %%edx,%%esp\n\
mov %%esp,%%edi\n\
rep movsl %%ds:(%%esi),%%es:(%%edi)\n\
add %%edx,%%edi\n\
call *%1\n\
mov %%edi,%%esp"\
: /* no outputs */\
: "c" ( &call ), "m" (functionPtr)\
: "%edi" , "%esi", "%edx", "%eax"\
);
#endif // GNUC vs non GNUC
return 1;
#elif !defined( AUTO_RPC_NO_ASM ) && ( defined( _M_X64 ) || defined( __x86_64__ ) || defined( _M_AMD64 ) )
#if AUTO_RPC_ABI == AUTO_RPC_ABI_WIN_AMD64
NaturalWord const paramv = reinterpret_cast<NaturalWord>( call.intParams );
_asm {
// rcx := number of qwords to copy
mov rcx, paramc
// r9 := 0
sub r9,r9
// rsi => our parameter list.
mov rsi, paramv
// r9 := -(number of qwords to copy)
sub r9,rcx
// Preparation to align the stack to 16 byte boundary
mov rdx,8
// rdi => projected bottom of stack
lea rdi,dword ptr[rsp + r9 * 8]
// figure out if stack needs aligning
and rdx,rdi
// save stack into rbx
mov rbx,rsp
// align stack
sub rdi,rdx
mov rsp,rdi
// rdx => our parameter list
mov rdx,rsi
//
// copy data - we copy all parameters, because we have to
// create a shadow area; and this way its easiest.
//
rep movs qword ptr es:[edi],qword ptr [esi]
// load registers
// rcx|xmm0, rdx|xmm1,r8|xmm2,r9|xmm3
mov rcx,qword ptr [rdx]
mov r8,qword ptr 16[rdx]
movq xmm0,rcx
mov r9,qword ptr 24[rdx]
movq xmm2,r8
mov rdx,qword ptr 8[rdx]
movq xmm3,r9
movq xmm1,rdx
// call the function
call functionPtr
// Restore the stack to its state, prior to our invocation -
// saved in rbx.
mov rsp,rbx
}
#elif AUTO_RPC_ABI == AUTO_RPC_ABI_SYSV_AMD64
//
// GCC won't generate a stack frame on higher optimization levels, so we don't touch it.
// on -O3 it inlines the code, breaking it because of the jump reference.
//
// I figure a 64-bit compiler will be recent enough to do Intel syntax. May need to change
// my mind on that. NB. Structure members are hard coded into this.
//
asm (\
".intel_syntax noprefix\n\
push rbx\n\
mov rax,rsi\n\
push r15\n\
mov ecx,dword ptr[rdi+8+8*8]\n\
lea rsi,[rdi+8+8*8+8]\n\
mov r15,rsp\n\
lea rbx,[rdi+8]\n\
sub r8,r8\n\
sub rsp,8\n\
sub rcx,6\n\
lea r9,[rsi + 6 * 8]\n\
jbe .L1\n\
sub r8,rcx\n\
mov rdx,8\n\
lea rdi,qword ptr[rsp + r8 * 8]\n\
and rdx,rdi\n\
mov rsi,r9\n\
sub rdi,rdx\n\
mov rsp,rdi\n\
rep movsq \n\
.L1:\n\
movq xmm0,[rbx]\n\
movq xmm1,[rbx+8]\n\
movq xmm2,[rbx+16]\n\
movq xmm3,[rbx+24]\n\
movq xmm4,[rbx+32]\n\
movq xmm5,[rbx+40]\n\
movq xmm6,[rbx+48]\n\
movq xmm7,[rbx+56]\n\
mov rdi,[r9-48]\n\
mov rsi,[r9-40]\n\
mov rdx,[r9-32]\n\
mov rcx,[r9-24]\n\
mov r8,[r9-16]\n\
mov r9,[r9-8]\n\
call rax\n\
mov rsp,r15\n\
pop r15\n\
pop rbx\n\
.att_syntax prefix"\
: /* no outputs */\
: "D" ( &call ), "S" (functionPtr)\
: "%rdx", "%rcx" , "%r8", "%r9", "%rax",\
"%xmm0", "%xmm1", "%xmm2", "%xmm3", "%xmm4", "%xmm5", "%xmm6", "%xmm7" );
// : "D", ( &call ), "c" ( &call.numIntParams ), "S" ( paramv ), "b" ( floatv ), "a" (functionPtr)
#else
#error unsupport ABI
#endif
return 1;
#else
// AUTO_RPC_NO_ASM or no x86-32/x86-64
//
// 4. Passing the parameters.
//
// The compiler knows how to call functions, so having sorted out the argument list,
// we just pass it to a function of the correct form - and let the compiler align stacks,
// load registers, place parameters where they should be.
//
// This is particularly important as GCC has control over the stack frame, and it can
// improperly frame it - for instance utilising red zones to save args, rather than pushing them.
// On PowerPC it must create the parameter passing area, too.
//
// The most brute force way, is to code a function call for every possible number of parameters
//
// switch( paramc ) {
// case 1: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord)) myfunc)( callParam[0] ); break;
// case 2: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord)) myfunc)( callParam[0], callParam[1] ); break;
// ...
// case 64: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord)) myfunc)( callParam[0], callParam[1], ... , callParam[63] ); break;
// }
//
// This is the only way to code WIN32 stdcall, for example, as the args must match exactly;
// and so the only way to call from C if you need to call WINAPI routines.
//
// 2) Fortunately most calling conventions allowing excessive args. So this means we could
// write something like below:
//
// ( (void(*)(...)) myfunc )( args[0],...,args[63] );
//
// And although this should work, its a huge performance penalty copying between memory
// locations for so many args.
//
// So we compromise - and do a stepped sequence. Noticing that the WIN64 ABI alwys requires
// space for three args anyway.
//
// And on SysV x64 systems, the first 6 args are passed in reg; so again, these are only
// copies into register, not memory copies. And finally that if we load word[n], word[n+1]
// is loaded into the cache - thus the overhead for loading is not as big as it might be.
//
// For most realistic cases, a dozen args would be excessive. Obviously, if you have
// a tested assembler equivalent, its probably better to use that.
//
//
#if AUTO_RPC_FLOAT_REG_PARAMS
if ( call.numRealParams == 0 )
#endif
{
if ( call.numIntParams <= 3 )
{
( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_3)) functionPtr )( AUTO_RPC_INT_ARGS_3( call ) );
return 1;
}
if ( call.numIntParams <= 6 )
{
( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_6)) functionPtr )( AUTO_RPC_INT_ARGS_6( call ) );
return 1;
}
if ( call.numIntParams <= 9 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_9))functionPtr)( AUTO_RPC_INT_ARGS_9( call ) );
return 1;
}
if ( call.numIntParams <= 12 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_12))functionPtr)( AUTO_RPC_INT_ARGS_12( call ) );
return 1;
}
if ( call.numIntParams <= 32 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_32))functionPtr)( AUTO_RPC_INT_ARGS_32( call ) );
return 1;
}
if ( call.numIntParams <= 64 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_64))functionPtr)( AUTO_RPC_INT_ARGS_64( call ) );
return 1;
}
}
#if AUTO_RPC_FLOAT_REG_PARAMS && !AUTO_RPC_ALLOC_SEPARATE_FLOATS
else
{
if ( call.numIntParams > 64 ) return 0;
switch( call.realMap )
{
// case 0: - no floats, never happens here.
case 1: ( (void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.intParams[1], call.intParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 2:
((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.realParams[1], call.intParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 3:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,NaturalWord,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.realParams[1], call.intParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 4: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.intParams[1], call.realParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 5:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.intParams[1], call.realParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 6:
((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.realParams[1], call.realParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 7:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,HardwareReal,NaturalWord,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.realParams[1], call.realParams[2], call.intParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 8: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.intParams[1], call.intParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 9:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.intParams[1], call.intParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 10:
((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.realParams[1], call.intParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 11:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,NaturalWord,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.realParams[1], call.intParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 12: ( (void(AUTO_RPC_CALLSPEC*)(NaturalWord,NaturalWord,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.intParams[1], call.realParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 13:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,NaturalWord,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.intParams[1], call.realParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 14:
((void(AUTO_RPC_CALLSPEC*)(NaturalWord,HardwareReal,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.intParams[0], call.realParams[1], call.realParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
case 15:
((void(AUTO_RPC_CALLSPEC*)(HardwareReal,HardwareReal,HardwareReal,HardwareReal,AUTO_RPC_NW_4_64))functionPtr)(
call.realParams[0], call.realParams[1], call.realParams[2], call.realParams[3],
AUTO_RPC_INT_ARGS_4_64( call )
);
break;
default: return 0;
}
}
#elif AUTO_RPC_FLOAT_REG_PARAMS
else
{
// we pass FLOAT args last for powerpc compatibility. And although it means we pass them twice,
// they should end up in the correct floating point register, with the rest of the integers in the
// correct place...
//
// NB if you want to write inline asm for powerpc, you'll have to be put it in a separate
// "naked" function to that uou can setup the parameter passing area and ensure its big enough.
// (GCC will delete functions that are unused - it will delete the body of functions that
// aren't called.)
//
if ( call.numIntParams <= 3 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_3,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_3( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) );
return 1;
}
if ( call.numIntParams <= 6 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_6,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_6( call ),AUTO_RPC_FLOAT_REG_ARGS( call ) );
return 1;
}
if ( call.numIntParams <= 9 )
{
((void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_9,AUTO_RPC_FLOAT_REG_TYPE))functionPtr)( AUTO_RPC_INT_ARGS_9( call ),AUTO_RPC_FLOAT_REG_ARGS( call ) );
return 1;
}
if ( call.numIntParams <= 12 )
{
( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_12,AUTO_RPC_FLOAT_REG_TYPE)) functionPtr )( AUTO_RPC_INT_ARGS_12( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) );
return 1;
}
if ( call.numIntParams <= 64 )
{
( (void(AUTO_RPC_CALLSPEC*)(AUTO_RPC_NW_64,AUTO_RPC_FLOAT_REG_TYPE)) functionPtr )( AUTO_RPC_INT_ARGS_64( call ), AUTO_RPC_FLOAT_REG_ARGS( call ) );
return 1;
}
}
#endif // AUTO_RPC_FLOAT_REG_PARAMS
return 0;
#endif // AUTO_RPC_NO_ASM
#else // AUTO_RPC_ABI
return 0;
#endif
}
// --8<---8<----8<----8<---END

1022
thirdparty/raknet/Source/Gen_RPC8.h vendored Normal file

File diff suppressed because it is too large Load Diff

179
thirdparty/raknet/Source/GetTime.cpp vendored Normal file
View File

@@ -0,0 +1,179 @@
/// \file
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "GetTime.h"
#ifdef _XBOX360
#include "Console1Includes.h" // Developers of a certain platform will know what to do here.
#elif defined(_WIN32)
#include <windows.h>
DWORD mProcMask;
DWORD mSysMask;
HANDLE mThread;
static LARGE_INTEGER yo;
#elif defined(_PS3)
#include "Console2Includes.h"
#include <sys/sys_time.h> // GetTime.cpp
#include <stdint.h> // GetTime.cpp
#include <sys/time_util.h> // GetTime.cpp
uint64_t ticksPerSecond;
uint64_t initialTime;
#else
#include <sys/time.h>
#include <unistd.h>
static timeval tp;
RakNetTimeNS initialTime;
#endif
static bool initialized=false;
int queryCount=0;
RakNetTime RakNet::GetTime( void )
{
return (RakNetTime)(GetTimeNS()/1000);
}
RakNetTimeNS RakNet::GetTimeNS( void )
{
#if defined(_PS3)
uint64_t curTime;
if ( initialized == false)
{
ticksPerSecond = _PS3_GetTicksPerSecond();
// Use the function to get elapsed ticks, this is a macro.
_PS3_GetElapsedTicks(curTime);
uint64_t quotient, remainder;
quotient=(curTime / ticksPerSecond);
remainder=(curTime % ticksPerSecond);
initialTime = (RakNetTimeNS) quotient*(RakNetTimeNS)1000000 + (remainder*(RakNetTimeNS)1000000 / ticksPerSecond);
initialized = true;
}
#elif defined(_WIN32)
// Win32
if ( initialized == false)
{
initialized = true;
#if !defined(_WIN32_WCE)
// Save the current process
HANDLE mProc = GetCurrentProcess();
// Get the current Affinity
#if _MSC_VER >= 1400 && defined (_M_X64)
GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask);
#else
GetProcessAffinityMask(mProc, &mProcMask, &mSysMask);
#endif
mThread = GetCurrentThread();
#endif // !defined(_WIN32_WCE)
QueryPerformanceFrequency( &yo );
}
// 01/12/08 - According to the docs "The frequency cannot change while the system is running." so this shouldn't be necessary
/*
if (++queryCount==200)
{
// Set affinity to the first core
SetThreadAffinityMask(mThread, 1);
QueryPerformanceFrequency( &yo );
// Reset affinity
SetThreadAffinityMask(mThread, mProcMask);
queryCount=0;
}
*/
#elif (defined(__GNUC__) || defined(__GCCXML__))
if ( initialized == false)
{
gettimeofday( &tp, 0 );
initialized=true;
// I do this because otherwise RakNetTime in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion
initialTime = ( tp.tv_sec ) * (RakNetTimeNS) 1000000 + ( tp.tv_usec );
}
#endif
#if defined(_PS3)
// Use the function to get elapsed ticks, this is a macro.
_PS3_GetElapsedTicks(curTime);
uint64_t quotient, remainder;
quotient=(curTime / ticksPerSecond);
remainder=(curTime % ticksPerSecond);
curTime = (RakNetTimeNS) quotient*(RakNetTimeNS)1000000 + (remainder*(RakNetTimeNS)1000000 / ticksPerSecond);
// Subtract from initialTime so the millisecond conversion does not underflow
return curTime - initialTime;
#elif defined(_WIN32)
RakNetTimeNS curTime;
static RakNetTimeNS lastQueryVal=(RakNetTimeNS)0;
static unsigned long lastTickCountVal = GetTickCount();
LARGE_INTEGER PerfVal;
#if !defined(_WIN32_WCE)
// Set affinity to the first core
SetThreadAffinityMask(mThread, 1);
#endif // !defined(_WIN32_WCE)
// Docs: On a multiprocessor computer, it should not matter which processor is called.
// However, you can get different results on different processors due to bugs in the basic input/output system (BIOS) or the hardware abstraction layer (HAL). To specify processor affinity for a thread, use the SetThreadAffinityMask function.
// Query the timer
QueryPerformanceCounter( &PerfVal );
#if !defined(_WIN32_WCE)
// Reset affinity
SetThreadAffinityMask(mThread, mProcMask);
#endif // !defined(_WIN32_WCE)
__int64 quotient, remainder;
quotient=((PerfVal.QuadPart) / yo.QuadPart);
remainder=((PerfVal.QuadPart) % yo.QuadPart);
curTime = (RakNetTimeNS) quotient*(RakNetTimeNS)1000000 + (remainder*(RakNetTimeNS)1000000 / yo.QuadPart);
#if !defined(_WIN32_WCE)
if (lastQueryVal==0)
{
// First call
lastQueryVal=curTime;
return curTime;
}
// To workaround http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days
unsigned long curTickCount = GetTickCount();
unsigned elapsedTickCount = curTickCount - lastTickCountVal;
RakNetTimeNS elapsedQueryVal = curTime - lastQueryVal;
if (elapsedQueryVal/1000 > elapsedTickCount+100)
{
curTime=lastQueryVal+elapsedTickCount*1000;
}
lastTickCountVal=curTickCount;
lastQueryVal=curTime;
#endif
return curTime;
#elif (defined(__GNUC__) || defined(__GCCXML__))
// GCC
RakNetTimeNS curTime;
gettimeofday( &tp, 0 );
curTime = ( tp.tv_sec ) * (RakNetTimeNS) 1000000 + ( tp.tv_usec );
// Subtract from initialTime so the millisecond conversion does not underflow
return curTime - initialTime;
#endif
}

33
thirdparty/raknet/Source/GetTime.h vendored Normal file
View File

@@ -0,0 +1,33 @@
/// \file
/// \brief Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __GET_TIME_H
#define __GET_TIME_H
#include "Export.h"
#include "RakNetTypes.h" // For RakNetTime
/// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common.
/// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes.
namespace RakNet
{
/// Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time.
RakNetTime RAK_DLL_EXPORT GetTime( void );
RakNetTimeNS RAK_DLL_EXPORT GetTimeNS( void );
}
#endif

View File

@@ -0,0 +1,191 @@
#include "RakAssert.h"
#include "GridSectorizer.h"
//#include <stdlib.h>
#include <math.h>
GridSectorizer::GridSectorizer()
{
grid=0;
}
GridSectorizer::~GridSectorizer()
{
if (grid)
delete [] grid;
}
void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY)
{
RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f);
if (grid)
delete [] grid;
cellOriginX=minX;
cellOriginY=minY;
gridWidth=maxX-minX;
gridHeight=maxY-minY;
gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth);
gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight);
// Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors.
cellWidth=gridWidth/gridCellWidthCount;
cellHeight=gridHeight/gridCellHeightCount;
invCellWidth = 1.0f / cellWidth;
invCellHeight = 1.0f / cellHeight;
#ifdef _USE_ORDERED_LIST
grid = new DataStructures::OrderedList<void*, void*>[gridCellWidthCount*gridCellHeightCount];
DataStructures::OrderedList<void*,void*>::IMPLEMENT_DEFAULT_COMPARISON();
#else
grid = new DataStructures::List<void*>[gridCellWidthCount*gridCellHeightCount];
#endif
}
void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY)
{
RakAssert(cellWidth>0.0f);
RakAssert(minX < maxX && minY < maxY);
int xStart, yStart, xEnd, yEnd, xCur, yCur;
xStart=WorldToCellXOffsetAndClamped(minX);
yStart=WorldToCellYOffsetAndClamped(minY);
xEnd=WorldToCellXOffsetAndClamped(maxX);
yEnd=WorldToCellYOffsetAndClamped(maxY);
for (xCur=xStart; xCur <= xEnd; ++xCur)
{
for (yCur=yStart; yCur <= yEnd; ++yCur)
{
#ifdef _USE_ORDERED_LIST
grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true);
#else
grid[yCur*gridCellWidthCount+xCur].Insert(entry);
#endif
}
}
}
#ifdef _USE_ORDERED_LIST
void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY)
{
RakAssert(cellWidth>0.0f);
RakAssert(minX <= maxX && minY <= maxY);
int xStart, yStart, xEnd, yEnd, xCur, yCur;
xStart=WorldToCellXOffsetAndClamped(minX);
yStart=WorldToCellYOffsetAndClamped(minY);
xEnd=WorldToCellXOffsetAndClamped(maxX);
yEnd=WorldToCellYOffsetAndClamped(maxY);
for (xCur=xStart; xCur <= xEnd; ++xCur)
{
for (yCur=yStart; yCur <= yEnd; ++yCur)
{
grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry);
}
}
}
void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY,
const float destMinX, const float destMinY, const float destMaxX, const float destMaxY)
{
RakAssert(cellWidth>0.0f);
RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY);
RakAssert(destMinX < destMaxX && destMinY < destMaxY);
if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false &&
PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false)
return;
int xStartSource, yStartSource, xEndSource, yEndSource;
int xStartDest, yStartDest, xEndDest, yEndDest;
int xCur, yCur;
xStartSource=WorldToCellXOffsetAndClamped(sourceMinX);
yStartSource=WorldToCellYOffsetAndClamped(sourceMinY);
xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX);
yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY);
xStartDest=WorldToCellXOffsetAndClamped(destMinX);
yStartDest=WorldToCellYOffsetAndClamped(destMinY);
xEndDest=WorldToCellXOffsetAndClamped(destMaxX);
yEndDest=WorldToCellYOffsetAndClamped(destMaxY);
// Remove source that is not in dest
for (xCur=xStartSource; xCur <= xEndSource; ++xCur)
{
for (yCur=yStartSource; yCur <= yEndSource; ++yCur)
{
if (xCur < xStartDest || xCur > xEndDest ||
yCur < yStartDest || yCur > yEndDest)
{
grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry);
}
}
}
// Add dest that is not in source
for (xCur=xStartDest; xCur <= xEndDest; ++xCur)
{
for (yCur=yStartDest; yCur <= yEndDest; ++yCur)
{
if (xCur < xStartSource || xCur > xEndSource ||
yCur < yStartSource || yCur > yEndSource)
{
grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true);
}
}
}
}
#endif
void GridSectorizer::GetEntries(DataStructures::List<void*>& intersectionList, const float minX, const float minY, const float maxX, const float maxY)
{
#ifdef _USE_ORDERED_LIST
DataStructures::OrderedList<void*, void*>* cell;
#else
DataStructures::List<void*>* cell;
#endif
int xStart, yStart, xEnd, yEnd, xCur, yCur;
unsigned index;
xStart=WorldToCellXOffsetAndClamped(minX);
yStart=WorldToCellYOffsetAndClamped(minY);
xEnd=WorldToCellXOffsetAndClamped(maxX);
yEnd=WorldToCellYOffsetAndClamped(maxY);
intersectionList.Clear(true);
for (xCur=xStart; xCur <= xEnd; ++xCur)
{
for (yCur=yStart; yCur <= yEnd; ++yCur)
{
cell = grid+yCur*gridCellWidthCount+xCur;
for (index=0; index < cell->Size(); ++index)
intersectionList.Insert(cell->operator [](index));
}
}
}
bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const
{
return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight;
}
int GridSectorizer::WorldToCellX(const float input) const
{
return (int)((input-cellOriginX)*invCellWidth);
}
int GridSectorizer::WorldToCellY(const float input) const
{
return (int)((input-cellOriginY)*invCellHeight);
}
int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const
{
int cell=WorldToCellX(input);
cell = cell > 0 ? cell : 0; // __max(cell,0);
cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell);
return cell;
}
int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const
{
int cell=WorldToCellY(input);
cell = cell > 0 ? cell : 0; // __max(cell,0);
cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell);
return cell;
}
void GridSectorizer::Clear(void)
{
int cur;
int count = gridCellWidthCount*gridCellHeightCount;
for (cur=0; cur<count;cur++)
grid[cur].Clear(true);
}

View File

@@ -0,0 +1,68 @@
#ifndef _GRID_SECTORIZER_H
#define _GRID_SECTORIZER_H
//#define _USE_ORDERED_LIST
#include "RakMemoryOverride.h"
#ifdef _USE_ORDERED_LIST
#include "DS_OrderedList.h"
#else
#include "DS_List.h"
#endif
class GridSectorizer : public RakNet::RakMemoryOverride
{
public:
GridSectorizer();
~GridSectorizer();
// _cellWidth, _cellHeight is the width and height of each cell in world units
// minX, minY, maxX, maxY are the world dimensions (can be changed to dynamically allocate later if needed)
void Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY);
// Adds a pointer to the grid with bounding rectangle dimensions
void AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY);
#ifdef _USE_ORDERED_LIST
// Removes a pointer, as above
void RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY);
// Adds and removes in one pass, more efficient than calling both functions consecutively
void MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY,
const float destMinX, const float destMinY, const float destMaxX, const float destMaxY);
#endif
// Adds to intersectionList all entries in a certain radius
void GetEntries(DataStructures::List<void*>& intersectionList, const float minX, const float minY, const float maxX, const float maxY);
void Clear(void);
protected:
int WorldToCellX(const float input) const;
int WorldToCellY(const float input) const;
int WorldToCellXOffsetAndClamped(const float input) const;
int WorldToCellYOffsetAndClamped(const float input) const;
// Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries
bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const;
float cellOriginX, cellOriginY;
float cellWidth, cellHeight;
float invCellWidth, invCellHeight;
float gridWidth, gridHeight;
int gridCellWidthCount, gridCellHeightCount;
// int gridWidth, gridHeight;
#ifdef _USE_ORDERED_LIST
DataStructures::OrderedList<void*, void*>* grid;
#else
DataStructures::List<void*>* grid;
#endif
};
#endif

View File

@@ -0,0 +1,189 @@
/// \file
/// \brief Contains HTTPConnection, used to communicate with web servers
///
/// This file is part of RakNet Copyright 2008 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#include "TCPInterface.h"
#include "HTTPConnection.h"
#include "RakSleep.h"
#include "RakString.h"
#include "RakAssert.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
using namespace RakNet;
HTTPConnection::HTTPConnection(TCPInterface& _tcp, const char *_host, unsigned short _port)
: tcp(_tcp), host(_host), port(_port), state(RAK_HTTP_INITIAL) {}
void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType)
{
if(state == RAK_HTTP_IDLE)
state = RAK_HTTP_ESTABLISHED;
else if(state == RAK_HTTP_INITIAL)
state = RAK_HTTP_STARTING;
else
return;
outgoing = data;
path = remote_path;
contentType=_contentType;
incoming.Clear();
}
bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data)
{
if(badResponses.IsEmpty())
return false;
if (code)
*code = badResponses.Peek().code;
if (data)
*data = badResponses.Pop().data;
return true;
}
bool HTTPConnection::InList(StatusCheckFunction func)
{
SystemAddress address = (tcp.*func)();
if(address == UNASSIGNED_SYSTEM_ADDRESS)
return false;
server = address;
return true;
}
void HTTPConnection::Update(void)
{
if(InList(&TCPInterface::HasCompletedConnectionAttempt))
state = RAK_HTTP_ESTABLISHED;
if(InList(&TCPInterface::HasFailedConnectionAttempt))
state = RAK_HTTP_STARTING; // retry
// normally, HTTP servers close the stream after sending data
if(InList(&TCPInterface::HasLostConnection))
state = RAK_HTTP_INITIAL;
if(state == RAK_HTTP_STARTING)
{
server = tcp.Connect(host, port, false);
state = RAK_HTTP_CONNECTING;
}
if(state == RAK_HTTP_ESTABLISHED)
{
RakString request("POST %s HTTP/1.0\r\n"
"Host: %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %u\r\n"
"\r\n"
"%s",
path.C_String(),
host.C_String(),
contentType.C_String(),
(unsigned) outgoing.GetLength(),
outgoing.C_String());
tcp.Send(request, (unsigned int) strlen(request), server);
state = RAK_HTTP_REQUEST_SENT;
}
}
RakString HTTPConnection::Read(void)
{
const char *start_of_body = strstr(incoming, "\r\n\r\n");
if(! start_of_body)
{
badResponses.Push(BadResponse(incoming, HTTPConnection::NoBody));
return RakString();
}
return RakString(start_of_body + 4);
}
SystemAddress HTTPConnection::GetServerAddress(void) const
{
return server;
}
bool HTTPConnection::ProcessFinalTCPPacket(Packet *packet)
{
RakAssert(packet);
// read all the packets possible
if(packet->systemAddress == server)
{
if(incoming.GetLength() == 0)
{
int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 "));
if(response_code > 299)
badResponses.Push(BadResponse(packet->data, response_code));
}
incoming += (char *)packet->data; // safe because TCPInterface Null-terminates
assert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes
const char *start_of_body = strstr(incoming, "\r\n\r\n");
// besides having the server close the connection, they may
// provide a length header and supply that many bytes
if(start_of_body && state == RAK_HTTP_REQUEST_SENT)
{
if (strstr((const char*) packet->data, "\r\nConnection: close\r\n"))
{
state = RAK_HTTP_IDLE;
}
else
{
long length_of_headers = (long)(start_of_body + 4 - incoming.C_String());
const char *length_header = strstr(incoming, "\r\nLength: ");
if(length_header)
{
long length = atol(length_header + 10) + length_of_headers;
if((long) incoming.GetLength() >= length)
state = RAK_HTTP_IDLE;
}
}
}
}
return IsBusy()==false;
}
bool HTTPConnection::IsBusy(void) const
{
return state != RAK_HTTP_IDLE && state != RAK_HTTP_INITIAL;
}
int HTTPConnection::GetState(void) const
{
return state;
}
HTTPConnection::~HTTPConnection(void)
{
tcp.CloseConnection(server);
}

View File

@@ -0,0 +1,127 @@
/// \file
/// \brief Contains HTTPConnection, used to communicate with web servers
///
/// This file is part of RakNet Copyright 2008 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __HTTP_CONNECTION
#define __HTTP_CONNECTION
#include "Export.h"
#include "RakString.h"
#include "RakMemoryOverride.h"
#include "RakNetTypes.h"
#include "DS_Queue.h"
class TCPInterface;
struct SystemAddress;
/// \brief Use HTTPConnection to communicate with a web server.
/// Start an instance of TCPInterface via the Start() command.
/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor.
/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server
/// This class will handle connecting and reconnecting as necessary.
///
/// Note that only one Post() can be handled at a time.
class RAK_DLL_EXPORT HTTPConnection : public RakNet::RakMemoryOverride
{
public:
/// Returns a HTTP object associated with this tcp connection
/// \pre tcp should already be started
HTTPConnection(TCPInterface& tcp, const char *host, unsigned short port=80);
virtual ~HTTPConnection();
/// Submit data to the HTTP server
/// HTTP only allows one request at a time per connection
///
/// \pre IsBusy()==false
/// \param path the path on the remote server you want to POST to. For example "mywebpage/index.html"
/// \param data A NULL terminated string to submit to the server
/// \param contentType "Content-Type:" passed to post.
void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded");
/// Get data returned by the HTTP server
/// If IsFinished()==false then this may be empty or a partial
/// response.
RakNet::RakString Read(void);
/// Call periodically to do time-based updates
void Update(void);
/// Returns the address of the server we are connected to
SystemAddress GetServerAddress(void) const;
/// Process an HTTP data packet returned from TCPInterface
/// Returns true when we have gotten all the data from the HTTP server.
/// If this returns true then it's safe to Post() another request
/// Deallocate the packet as usual via TCPInterface
/// \param packet NULL or a packet associated with our host and port
/// \return true when all data from one Post() has been read.
bool ProcessFinalTCPPacket(Packet *packet);
/// Results of HTTP requests. Standard response codes are < 999
/// ( define HTTP codes and our internal codes as needed )
enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 };
HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;}
/// Encapsulates a raw HTTP response and response code
struct BadResponse
{
public:
BadResponse() {code=0;}
BadResponse(const unsigned char *_data, int _code)
: data((const char *)_data), code(_code) {}
BadResponse(const char *_data, int _code)
: data(_data), code(_code) {}
operator int () const { return code; }
RakNet::RakString data;
int code; // ResponseCodes
};
/// Queued events of failed exchanges with the HTTP server
bool HasBadResponse(int *code, RakNet::RakString *data);
/// Returns false if the connection is not doing anything else
bool IsBusy(void) const;
/// \internal
int GetState(void) const;
private:
SystemAddress server;
TCPInterface& tcp;
RakNet::RakString host;
unsigned short port;
enum { RAK_HTTP_INITIAL,
RAK_HTTP_STARTING,
RAK_HTTP_CONNECTING,
RAK_HTTP_ESTABLISHED,
RAK_HTTP_REQUEST_SENT,
RAK_HTTP_IDLE } state;
RakNet::RakString outgoing, incoming, path, contentType;
DataStructures::Queue<BadResponse> badResponses;
void Process(Packet *packet); // the workhorse
// this helps check the various status lists in TCPInterface
typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void);
bool InList(StatusCheckFunction func);
};
#endif

View File

@@ -0,0 +1,49 @@
#include "InlineFunctor.h"
void InlineFunctor::HandleResult(bool wasCancelled, void *context)
{
ifp->Pop(callDepth);
}
InlineFunctorProcessor::InlineFunctorProcessor()
{
}
InlineFunctorProcessor::~InlineFunctorProcessor()
{
StopThreads(false);
}
void InlineFunctorProcessor::StartThreads(int numThreads)
{
functionThread.StartThreads(numThreads);
}
void InlineFunctorProcessor::StopThreads(bool blockOnCurrentProcessing)
{
functionThread.StopThreads(blockOnCurrentProcessing);
}
void InlineFunctorProcessor::YieldOnFunctor(InlineFunctor *inlineFunctor)
{
inlineFunctor->callDepth=GetCallDepth();
inlineFunctor->ifp=this;
functionThread.Push(inlineFunctor);
completedThreads.Push(false);
}
bool InlineFunctorProcessor::UpdateIFP(void)
{
functionThread.CallResultHandlers();
if (completedThreads.Size() && completedThreads[completedThreads.Size()-1]==true)
{
completedThreads.Pop();
return true;
}
return false;
}
void InlineFunctorProcessor::Pop(int threadCallDepth)
{
completedThreads[threadCallDepth]=true;
}
unsigned InlineFunctorProcessor::GetCallDepth(void) const
{
return completedThreads.Size();
}

View File

@@ -0,0 +1,61 @@
#include "FunctionThread.h"
#include "DS_List.h"
class InlineFunctorProcessor;
/// A base class to derive functors from for the InlineFunctorProcessor system
class InlineFunctor : public RakNet::Functor
{
protected:
/// \internal - Calls InlineFunctorProcessor to signal that the functor is done
virtual void HandleResult(bool wasCancelled, void *context);
/// Tracks the call depth when this functor was pushed. It allows the system to return from functions in the correct order
int callDepth;
/// Pointer to the calling instance of InlineFunctorProcessor
InlineFunctorProcessor *ifp;
friend class InlineFunctorProcessor;
};
/// A base class that will allow you to call YieldOnFunctor() from within a function, and continue with that function when the asynchronous processing has completed
class InlineFunctorProcessor
{
public:
InlineFunctorProcessor();
~InlineFunctorProcessor();
/// Start the threads. Should call this first
/// \param[in] numThreads How many worker threads to start
/// \note If only one thread is started, then the calls to YieldOnFunctor will process in that order
void StartThreads(int numThreads);
/// Stop the threads
/// \param[in] blockOnCurrentProcessing Wait for the current processing to finish?
void StopThreads(bool blockOnCurrentProcessing);
/// Yield processing in the current function, continuing with the function implemented by CallYieldFunction
/// When the functor completes, this function will return and the caller will continue processing
/// \param[in] inlineFunctor A class that implements Functor::Process() to perform processing that can work asynchronously, such as loading a file or doing a database call
void YieldOnFunctor(InlineFunctor *inlineFunctor);
/// \internal
/// If the functor is done, continue processing the caller
/// \return True if the topmost functor has completed, false otherwise
bool UpdateIFP(void);
/// \internal
/// Notify the caller that the functor is done
void Pop(int threadCallDepth);
protected:
/// Returns the number of functors that were passed to the system
unsigned GetCallDepth(void) const;
/// Used to create a thread that processes functors
RakNet::FunctionThread functionThread;
/// Tracks which threads have been completed
DataStructures::List<bool> completedThreads;
};

View File

@@ -0,0 +1,91 @@
/// \file
/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __INTERNAL_PACKET_H
#define __INTERNAL_PACKET_H
#include "PacketPriority.h"
#include "RakNetTypes.h"
#include "RakMemoryOverride.h"
typedef unsigned short SplitPacketIdType;
typedef unsigned int SplitPacketIndexType;
/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables
/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth!
/// Use the smallest possible value, such that you send no more than rangeof(MessageNumberType) / GetTimeoutTime() packets per second
/// For the default value of 10 seconds, this is
/// unsigned char - 25.5 packets per second
/// unsigned short - 6553.5 packets per second
/// unsigned int - You'll run out of memory first.
typedef unsigned int MessageNumberType;
/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables
/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth!
typedef MessageNumberType OrderingIndexType;
typedef RakNetTime RemoteSystemTimeType;
/// Holds a user message, and related information
/// Don't use a constructor or destructor, due to the memory pool I am using
struct InternalPacket : public RakNet::RakMemoryOverride//<InternalPacket>
{
///True if this is an acknowledgment packet
//bool isAcknowledgement;
/// A unique numerical identifier given to this user message. Used to identify messages on the network
MessageNumberType messageNumber;
/// Identifies the order in which this number was sent. Used locally
MessageNumberType messageInternalOrder;
/// Has this message number been assigned yet? We don't assign until the message is actually sent.
/// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap.
/// This causes performance problems and causes those messages to timeout.
bool messageNumberAssigned;
/// Used only for tracking packetloss and windowing internally, this is the aggreggate packet number that a message was last sent in
unsigned packetNumber;
/// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update.
// bool allowWindowUpdate;
///The priority level of this packet
PacketPriority priority;
///What type of reliability algorithm to use with this packet
PacketReliability reliability;
///What ordering channel this packet is on, if the reliability type uses ordering channels
unsigned char orderingChannel;
///The ID used as identification for ordering channels
OrderingIndexType orderingIndex;
///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection.
SplitPacketIdType splitPacketId;
///If this is a split packet, the index into the array of subsplit packets
SplitPacketIndexType splitPacketIndex;
///The size of the array of subsplit packets
SplitPacketIndexType splitPacketCount;
///When this packet was created
RakNetTimeNS creationTime;
///The next time to take action on this packet
RakNetTimeNS nextActionTime;
// If this was a reliable packet, it included the ping time, to be sent back in an ack
//RakNetTimeNS remoteSystemTime;
//RemoteSystemTimeType remoteSystemTime;
///How many bits the data is
BitSize_t dataBitLength;
///Buffer is a pointer to the actual data, assuming this packet has data at all
unsigned char *data;
};
#endif

45
thirdparty/raknet/Source/Itoa.cpp vendored Normal file
View File

@@ -0,0 +1,45 @@
// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function.
// I modified it to remove the std dependencies.
char* Itoa( int value, char* result, int base )
{
// check that the base if valid
if (base < 2 || base > 16) { *result = 0; return result; }
char* out = result;
int quotient = value;
int absQModB;
do {
// KevinJ - get rid of this dependency
//*out = "0123456789abcdef"[ std::abs( quotient % base ) ];
absQModB=quotient % base;
if (absQModB < 0)
absQModB=-absQModB;
*out = "0123456789abcdef"[ absQModB ];
++out;
quotient /= base;
} while ( quotient );
// Only apply negative sign for base 10
if ( value < 0 && base == 10) *out++ = '-';
// KevinJ - get rid of this dependency
// std::reverse( result, out );
*out = 0;
// KevinJ - My own reverse code
char *start = result;
char temp;
out--;
while (start < out)
{
temp=*start;
*start=*out;
*out=temp;
start++;
out--;
}
return result;
}

8
thirdparty/raknet/Source/Itoa.h vendored Normal file
View File

@@ -0,0 +1,8 @@
#ifndef __RAK_ITOA_H
#define __RAK_ITOA_H
#include "Export.h"
RAK_DLL_EXPORT char* Itoa( int value, char* result, int base );
#endif

84
thirdparty/raknet/Source/Kbhit.h vendored Normal file
View File

@@ -0,0 +1,84 @@
/*****************************************************************************
kbhit() and getch() for Linux/UNIX
Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer
Release date: ?
This code is public domain (no copyright).
You can do whatever you want with it.
*****************************************************************************/
#if !defined(linux)
#include <conio.h> /* kbhit(), getch() */
#else
#include <sys/time.h> /* struct timeval, select() */
/* ICANON, ECHO, TCSANOW, struct termios */
#include <termios.h> /* tcgetattr(), tcsetattr() */
#include <stdlib.h> /* atexit(), exit() */
#include <unistd.h> /* read() */
#include <stdio.h> /* printf() */
#include <string.h> /* memcpy */
static struct termios g_old_kbd_mode;
/*****************************************************************************
*****************************************************************************/
static void cooked(void)
{
tcsetattr(0, TCSANOW, &g_old_kbd_mode);
}
/*****************************************************************************
*****************************************************************************/
static void raw(void)
{
static char init;
/**/
struct termios new_kbd_mode;
if(init)
return;
/* put keyboard (stdin, actually) in raw, unbuffered mode */
tcgetattr(0, &g_old_kbd_mode);
memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));
new_kbd_mode.c_lflag &= ~(ICANON | ECHO);
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
tcsetattr(0, TCSANOW, &new_kbd_mode);
/* when we exit, go back to normal, "cooked" mode */
atexit(cooked);
init = 1;
}
/*****************************************************************************
*****************************************************************************/
static int kbhit(void)
{
struct timeval timeout;
fd_set read_handles;
int status;
raw();
/* check stdin (fd 0) for activity */
FD_ZERO(&read_handles);
FD_SET(0, &read_handles);
timeout.tv_sec = timeout.tv_usec = 0;
status = select(0 + 1, &read_handles, NULL, NULL, &timeout);
if(status < 0)
{
printf("select() failed in kbhit()\n");
exit(1);
}
return status;
}
/*****************************************************************************
*****************************************************************************/
static int getch(void)
{
unsigned char temp;
raw();
/* stdin = fd 0 */
if(read(0, &temp, 1) != 1)
return 0;
return temp;
}
#endif

View File

@@ -0,0 +1,133 @@
#include "LightweightDatabaseClient.h"
#include "StringCompressor.h"
#include "MessageIdentifiers.h"
#include "RakPeerInterface.h"
#include "TableSerializer.h"
#include "BitStream.h"
#include "RakAssert.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
LightweightDatabaseClient::LightweightDatabaseClient()
{
rakPeer=0;
}
LightweightDatabaseClient::~LightweightDatabaseClient()
{
}
void LightweightDatabaseClient::QueryTable(const char *tableName, const char *queryPassword, const char **columnNamesSubset, unsigned char numColumnSubset, DatabaseFilter *filter, unsigned char numFilters, unsigned *rowIds, unsigned char numRowIDs, SystemAddress systemAddress, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
RakNet::BitStream out;
out.Write((MessageID)ID_DATABASE_QUERY_REQUEST);
stringCompressor->EncodeString(tableName, _SIMPLE_DATABASE_TABLE_NAME_LENGTH, &out);
if (queryPassword && queryPassword[0])
{
out.Write(true);
// This is sent in plain text. I can do this securely but it's not worth the trouble.
// Use secure connections if you want security.
stringCompressor->EncodeString(queryPassword, _SIMPLE_DATABASE_PASSWORD_LENGTH, &out);
}
else
out.Write(false);
out.Write(numColumnSubset);
unsigned i;
for (i=0; i < numColumnSubset; i++)
{
stringCompressor->EncodeString(columnNamesSubset[i],256,&out);
}
out.Write(numFilters);
for (i=0; i < numFilters; i++)
{
RakAssert((int)filter[i].operation<=(int)DataStructures::Table::QF_NOT_EMPTY);
filter[i].Serialize(&out);
}
out.Write(numRowIDs);
for (i=0; i < numRowIDs; i++)
out.Write(rowIds[i]);
rakPeer->Send(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,systemAddress, broadcast);
}
void LightweightDatabaseClient::RemoveRow(const char *tableName, const char *removePassword, unsigned rowId, SystemAddress systemAddress, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
RakNet::BitStream out;
out.Write((MessageID)ID_DATABASE_REMOVE_ROW);
stringCompressor->EncodeString(tableName, _SIMPLE_DATABASE_TABLE_NAME_LENGTH, &out);
if (removePassword && removePassword[0])
{
out.Write(true);
// This is sent in plain text. I can do this securely but it's not worth the trouble.
// Use secure connections if you want security.
stringCompressor->EncodeString(removePassword, _SIMPLE_DATABASE_PASSWORD_LENGTH, &out);
}
else
out.Write(false);
out.Write(rowId);
rakPeer->Send(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,systemAddress, broadcast);
}
void LightweightDatabaseClient::UpdateRow(const char *tableName, const char *updatePassword, RowUpdateMode updateMode, bool hasRowId, unsigned rowId, DatabaseCellUpdate *cellUpdates, unsigned char numCellUpdates, SystemAddress systemAddress, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
if (cellUpdates==0 || numCellUpdates==0)
return;
RakNet::BitStream out;
out.Write((MessageID)ID_DATABASE_UPDATE_ROW);
stringCompressor->EncodeString(tableName, _SIMPLE_DATABASE_TABLE_NAME_LENGTH, &out);
if (updatePassword && updatePassword[0])
{
out.Write(true);
// This is sent in plain text. I can do this securely but it's not worth the trouble.
// Use secure connections if you want security.
stringCompressor->EncodeString(updatePassword, _SIMPLE_DATABASE_PASSWORD_LENGTH, &out);
}
else
out.Write(false);
out.Write((unsigned char) updateMode);
out.Write(hasRowId);
if (hasRowId)
out.Write(rowId);
out.Write(numCellUpdates);
unsigned i;
for (i=0; i < numCellUpdates; i++)
cellUpdates[i].Serialize(&out);
rakPeer->Send(&out, HIGH_PRIORITY, RELIABLE_ORDERED,0,systemAddress, broadcast);
}
PluginReceiveResult LightweightDatabaseClient::OnReceive(RakPeerInterface *peer, Packet *packet)
{
(void) packet;
(void) peer;
return RR_CONTINUE_PROCESSING;
}
void LightweightDatabaseClient::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,88 @@
/// \file
/// \brief Contains the client interface to the simple database included with RakNet, useful for a server browser or a lobby server.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __LIGHTWEIGHT_DATABASE_CLIENT_H
#define __LIGHTWEIGHT_DATABASE_CLIENT_H
#include "Export.h"
#include "PluginInterface.h"
#include "LightweightDatabaseCommon.h"
class RakPeerInterface;
struct Packet;
/// \defgroup SIMPLE_DATABSE_GROUP LightweightDatabase
/// \ingroup PLUGINS_GROUP
/// \brief The client interface to the simple database included with RakNet, useful for a server browser or a lobby server.
/// \ingroup SIMPLE_DATABSE_GROUP
class RAK_DLL_EXPORT LightweightDatabaseClient : public PluginInterface
{
public:
/// Constructor
LightweightDatabaseClient();
/// Destructor
virtual ~LightweightDatabaseClient();
/// Sends a query to a remote table.
/// The remote system should return ID_DATABASE_QUERY_REPLY, ID_DATABASE_UNKNOWN_TABLE, or ID_DATABASE_INCORRECT_PASSWORD
/// \note If the remote table uses a password and you send the wrong one you will be silently disconnected and banned for one second.
/// \param[in] tableName String name of the remote table. Case sensitive.
/// \param[in] queryPassword Password to query the remote table, if any.
/// \param[in] columnSubset An array of string names of the columns to query. The resulting table will return the columns in this same order. Pass 0 for all columns.
/// \param[in] numColumnSubset The number of elements in the columnSubset array
/// \param[in] filter Filters to apply to the query. For each row the filters are applied. All filters must be passed for the row to be returned. Pass 0 for no filters.
/// \param[in] numFilters The number of elements in the filter array
/// \param[in] rowIds Which particular rows to return. Pass 0 for all rows.
/// \param[in] numRowIDs The number of elements in the rowIds array
/// \param[in] systemAddress Which system to send to.
/// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send
void QueryTable(const char *tableName, const char *queryPassword, const char **columnNamesSubset, unsigned char numColumnSubset, DatabaseFilter *filter, unsigned char numFilters, unsigned *rowIds, unsigned char numRowIDs, SystemAddress systemAddress, bool broadcast);
/// Sets one or more values in a new or existing row, assuming the server allows row creation and updates.
/// No response is returned by the server.
/// \param[in] tableName String name of the remote table. Case sensitive.
/// \param[in] updatePassword Password to update the remote table, if any.
/// \param[in] updateMode See RowUpdateMode in LightweightDatabaseCommon.h . This determines if to update an existing or new row.
/// \param[in] hasRowId True if a valid value was passed for \a rowId, false otherwise. If false, will lookup the row by system address. Required if adding a new row and the remote system does not automatically create rowIDs.
/// \param[in] rowId The rowID of the new or existing row.
/// \param[in] cellUpdates An array of DatabaseCellUpdate structures containing the values to write to the remote row.
/// \param[in] numCellUpdates The number of elements in the cellUpdates array
/// \param[in] systemAddress Which system to send to.
/// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send
void UpdateRow(const char *tableName, const char *updatePassword, RowUpdateMode updateMode, bool hasRowId, unsigned rowId, DatabaseCellUpdate *cellUpdates, unsigned char numCellUpdates, SystemAddress systemAddress, bool broadcast);
/// Removes a remote row, assuming the server allows row removal.
/// No response is returned by the server.
/// \param[in] tableName String name of the remote table. Case sensitive.
/// \param[in] removePassword Password to remove rows from the remote table, if any.
/// \param[in] rowId The rowID of the existing row.
/// \param[in] systemAddress Which system to send to.
/// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send
void RemoveRow(const char *tableName, const char *removePassword, unsigned rowId, SystemAddress systemAddress, bool broadcast);
/// \internal For plugin handling
void OnAttach(RakPeerInterface *peer);
/// \internal For plugin handling
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
protected:
RakPeerInterface *rakPeer;
};
#endif

View File

@@ -0,0 +1,45 @@
#include "TableSerializer.h"
#include "LightweightDatabaseCommon.h"
#include "BitStream.h"
#include "StringCompressor.h"
void DatabaseFilter::Serialize(RakNet::BitStream *out)
{
stringCompressor->EncodeString(columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out);
out->Write((unsigned char)columnType);
out->Write((unsigned char)operation);
if (operation!=DataStructures::Table::QF_IS_EMPTY && operation!=DataStructures::Table::QF_NOT_EMPTY)
{
assert(cellValue.isEmpty==false);
TableSerializer::SerializeCell(out, &cellValue, columnType);
}
}
bool DatabaseFilter::Deserialize(RakNet::BitStream *in)
{
unsigned char temp;
stringCompressor->DecodeString(columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, in);
in->Read(temp);
columnType=(DataStructures::Table::ColumnType)temp;
if (in->Read(temp)==false)
return false;
operation=(DataStructures::Table::FilterQueryType)temp;
if (operation!=DataStructures::Table::QF_IS_EMPTY && operation!=DataStructures::Table::QF_NOT_EMPTY)
{
return TableSerializer::DeserializeCell(in, &cellValue, columnType);
}
return true;
}
void DatabaseCellUpdate::Serialize(RakNet::BitStream *out)
{
stringCompressor->EncodeString(columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, out);
out->Write((unsigned char)columnType);
TableSerializer::SerializeCell(out, &cellValue, columnType);
}
bool DatabaseCellUpdate::Deserialize(RakNet::BitStream *in)
{
unsigned char temp;
stringCompressor->DecodeString(columnName, _TABLE_MAX_COLUMN_NAME_LENGTH, in);
in->Read(temp);
columnType=(DataStructures::Table::ColumnType)temp;
return TableSerializer::DeserializeCell(in, &cellValue, columnType);
}

View File

@@ -0,0 +1,53 @@
#ifndef __SIMPLE_DATABASE_COMMON_H
#define __SIMPLE_DATABASE_COMMON_H
#include "DS_Table.h"
namespace RakNet
{
class BitStream;
};
#define _SIMPLE_DATABASE_PASSWORD_LENGTH 32
#define _SIMPLE_DATABASE_TABLE_NAME_LENGTH 32
#define SYSTEM_ID_COLUMN_NAME "__SystemAddress"
#define LAST_PING_RESPONSE_COLUMN_NAME "__lastPingResponseTime"
#define NEXT_PING_SEND_COLUMN_NAME "__nextPingSendTime"
struct DatabaseFilter
{
void Serialize(RakNet::BitStream *out);
bool Deserialize(RakNet::BitStream *in);
DataStructures::Table::Cell cellValue;
DataStructures::Table::FilterQueryType operation;
DataStructures::Table::ColumnType columnType;
char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH];
};
/// The value to write to a cell in a remote database.
struct DatabaseCellUpdate
{
void Serialize(RakNet::BitStream *out);
bool Deserialize(RakNet::BitStream *in);
DataStructures::Table::Cell cellValue;
DataStructures::Table::ColumnType columnType;
char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH];
};
enum RowUpdateMode
{
// Only update an existing row. rowId is required.
RUM_UPDATE_EXISTING_ROW,
// Update an existing row if present - otherwise add a new row. rowId is required.
RUM_UPDATE_OR_ADD_ROW,
// Add a new row. If rowId is passed then the row will only be added if this id doesn't already exist.
// If rowId is not passed and the table requires a rowId the creation fails.
RUM_ADD_NEW_ROW,
};
#endif

View File

@@ -0,0 +1,670 @@
#include "LightweightDatabaseServer.h"
#include "MessageIdentifiers.h"
#include "BitStream.h"
#include "StringCompressor.h"
#include "RakPeerInterface.h"
#include "TableSerializer.h"
#include "RakAssert.h"
#include "GetTime.h"
#include "Rand.h"
static const int SEND_PING_INTERVAL=15000;
static const int DROP_SERVER_INTERVAL=75000;
#ifdef _MSC_VER
#pragma warning( push )
#endif
int LightweightDatabaseServer::DatabaseTableComp( char* const &key1, char* const &key2 )
{
return strcmp(key1, key2);
}
LightweightDatabaseServer::LightweightDatabaseServer()
{
}
LightweightDatabaseServer::~LightweightDatabaseServer()
{
Clear();
}
DataStructures::Table *LightweightDatabaseServer::GetTable(char *tableName)
{
if (database.Has(tableName))
return &(database.Get(tableName)->table);
return 0;
}
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *LightweightDatabaseServer::GetTableRows(char *tableName)
{
if (database.Has(tableName))
database.Get(tableName)->table.GetRows().GetListHead();
return 0;
}
DataStructures::Table* LightweightDatabaseServer::AddTable(char *tableName,
bool allowRemoteQuery,
bool allowRemoteUpdate,
bool allowRemoteRemove,
const char *queryPassword,
const char *updatePassword,
const char *removePassword,
bool oneRowPerSystemAddress,
bool onlyUpdateOwnRows,
bool removeRowOnPingFailure,
bool removeRowOnDisconnect,
bool autogenerateRowIDs)
{
if (tableName==0 || tableName[0]==0)
return 0;
if (database.Has(tableName))
return 0;
DatabaseTable *databaseTable = new DatabaseTable;
strncpy(databaseTable->tableName, tableName, _SIMPLE_DATABASE_TABLE_NAME_LENGTH-1);
databaseTable->tableName[_SIMPLE_DATABASE_TABLE_NAME_LENGTH-1]=0;
if (allowRemoteUpdate)
{
strncpy(databaseTable->updatePassword, updatePassword, _SIMPLE_DATABASE_PASSWORD_LENGTH-1);
databaseTable->updatePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH-1]=0;
}
else
databaseTable->updatePassword[0]=0;
if (allowRemoteQuery)
{
strncpy(databaseTable->queryPassword, queryPassword, _SIMPLE_DATABASE_PASSWORD_LENGTH-1);
databaseTable->queryPassword[_SIMPLE_DATABASE_PASSWORD_LENGTH-1]=0;
}
else
databaseTable->queryPassword[0]=0;
if (allowRemoteRemove)
{
strncpy(databaseTable->removePassword, removePassword, _SIMPLE_DATABASE_PASSWORD_LENGTH-1);
databaseTable->removePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH-1]=0;
}
else
databaseTable->removePassword[0]=0;
if (allowRemoteUpdate)
{
databaseTable->allowRemoteUpdate=true;
databaseTable->oneRowPerSystemAddress=oneRowPerSystemAddress;
databaseTable->onlyUpdateOwnRows=onlyUpdateOwnRows;
databaseTable->removeRowOnPingFailure=removeRowOnPingFailure;
databaseTable->removeRowOnDisconnect=removeRowOnDisconnect;
}
else
{
// All these parameters are related to IP tracking, which is not done if remote updates are not allowed
databaseTable->allowRemoteUpdate=true;
databaseTable->oneRowPerSystemAddress=false;
databaseTable->onlyUpdateOwnRows=false;
databaseTable->removeRowOnPingFailure=false;
databaseTable->removeRowOnDisconnect=false;
}
databaseTable->nextRowId=0;
databaseTable->nextRowPingCheck=0;
databaseTable->autogenerateRowIDs=autogenerateRowIDs;
databaseTable->allowRemoteRemove=allowRemoteRemove;
databaseTable->allowRemoteQuery=allowRemoteQuery;
database.SetNew(databaseTable->tableName, databaseTable);
if ( oneRowPerSystemAddress || onlyUpdateOwnRows || removeRowOnPingFailure || removeRowOnDisconnect)
databaseTable->SystemAddressColumnIndex=databaseTable->table.AddColumn(SYSTEM_ID_COLUMN_NAME, DataStructures::Table::BINARY);
else
databaseTable->SystemAddressColumnIndex=(unsigned) -1;
if (databaseTable->removeRowOnPingFailure)
{
databaseTable->lastPingResponseColumnIndex=databaseTable->table.AddColumn(LAST_PING_RESPONSE_COLUMN_NAME, DataStructures::Table::NUMERIC);
databaseTable->nextPingSendColumnIndex=databaseTable->table.AddColumn(NEXT_PING_SEND_COLUMN_NAME, DataStructures::Table::NUMERIC);
}
else
{
databaseTable->lastPingResponseColumnIndex=(unsigned) -1;
databaseTable->nextPingSendColumnIndex=(unsigned) -1;
}
return &(databaseTable->table);
}
bool LightweightDatabaseServer::RemoveTable(char *tableName)
{
LightweightDatabaseServer::DatabaseTable *databaseTable;
databaseTable = database.Get(tableName);
if (databaseTable==0)
return false;
// Be sure to call Delete on database before I do the actual pointer deletion since the key won't be valid after that time.
database.Delete(tableName);
databaseTable->table.Clear();
delete databaseTable;
return true;
}
void LightweightDatabaseServer::Clear(void)
{
unsigned i;
for (i=0; i < database.Size(); i++)
{
database[i]->table.Clear();
delete database[i];
}
database.Clear();
}
unsigned LightweightDatabaseServer::GetAndIncrementRowID(char *tableName)
{
LightweightDatabaseServer::DatabaseTable *databaseTable;
databaseTable = database.Get(tableName);
RakAssert(databaseTable);
RakAssert(databaseTable->autogenerateRowIDs==true);
return ++(databaseTable->nextRowId) - 1;
}
void LightweightDatabaseServer::OnAttach(RakPeerInterface *peer)
{
(void) peer;
}
void LightweightDatabaseServer::Update(RakPeerInterface *peer)
{
RakNetTime time=0;
DatabaseTable *databaseTable;
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur;
unsigned i,j;
DataStructures::Table::Row* row;
DataStructures::List<unsigned> removeList;
SystemAddress systemAddress;
// periodic ping if removing system that do not respond to pings.
for (i=0; i < database.Size(); i++)
{
databaseTable=database[i];
if (databaseTable->removeRowOnPingFailure)
{
// Reading the time is slow - only do it once if necessary.
if (time==0)
time = RakNet::GetTime();
if (databaseTable->nextRowPingCheck < time)
{
databaseTable->nextRowPingCheck=time+1000+(randomMT()%1000);
DataStructures::BPlusTree<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> &rows = databaseTable->table.GetRows();
cur = rows.GetListHead();
while (cur)
{
// Mark dropped entities
for (j=0; j < (unsigned)cur->size; j++)
{
row = cur->data[j];
if (time - (unsigned int) row->cells[databaseTable->lastPingResponseColumnIndex]->i > (unsigned int) DROP_SERVER_INTERVAL)
removeList.Insert(cur->keys[j]);
}
cur=cur->next;
}
// Remove dropped entities
for (j=0; j < removeList.Size(); j++)
databaseTable->table.RemoveRow(removeList[i]);
removeList.Clear(true);
cur = rows.GetListHead();
// Ping remaining entities if they are not connected. If they are connected just increase the ping interval.
while (cur)
{
for (j=0; j < (unsigned)cur->size; j++)
{
row = cur->data[j];
if (row->cells[databaseTable->nextPingSendColumnIndex]->i < (int) time)
{
row->cells[databaseTable->SystemAddressColumnIndex]->Get((char*)&systemAddress, 0);
if (peer->IsConnected(systemAddress)==false)
{
peer->Ping(systemAddress.ToString(false), systemAddress.port, false);
}
else
{
// Consider the fact that they are connected to be a ping response
row->cells[databaseTable->lastPingResponseColumnIndex]->i=time;
}
row->cells[databaseTable->nextPingSendColumnIndex]->i=time+SEND_PING_INTERVAL+(randomMT()%1000);
}
}
cur=cur->next;
}
}
}
}
}
PluginReceiveResult LightweightDatabaseServer::OnReceive(RakPeerInterface *peer, Packet *packet)
{
switch (packet->data[0])
{
case ID_DATABASE_QUERY_REQUEST:
OnQueryRequest(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_DATABASE_UPDATE_ROW:
OnUpdateRow(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_DATABASE_REMOVE_ROW:
OnRemoveRow(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
case ID_DISCONNECTION_NOTIFICATION:
case ID_CONNECTION_LOST:
RemoveRowsFromIP(packet->systemAddress);
return RR_CONTINUE_PROCESSING;
case ID_PONG:
OnPong(peer, packet);
return RR_CONTINUE_PROCESSING;
}
return RR_CONTINUE_PROCESSING;
}
void LightweightDatabaseServer::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
}
void LightweightDatabaseServer::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
{
(void) peer;
RemoveRowsFromIP(systemAddress);
}
void LightweightDatabaseServer::OnQueryRequest(RakPeerInterface *peer, Packet *packet)
{
RakNet::BitStream inBitstream(packet->data, packet->length, false);
LightweightDatabaseServer::DatabaseTable *databaseTable = DeserializeClientHeader(&inBitstream, peer, packet, 0);
if (databaseTable==0)
return;
if (databaseTable->allowRemoteQuery==false)
return;
unsigned char numColumnSubset;
RakNet::BitStream outBitstream;
unsigned i;
if (inBitstream.Read(numColumnSubset)==false)
return;
unsigned char columnName[256];
unsigned columnIndicesSubset[256];
unsigned columnIndicesCount;
for (i=0,columnIndicesCount=0; i < numColumnSubset; i++)
{
stringCompressor->DecodeString((char*)columnName, 256, &inBitstream);
unsigned colIndex = databaseTable->table.ColumnIndex((char*)columnName);
if (colIndex!=(unsigned)-1)
columnIndicesSubset[columnIndicesCount++]=colIndex;
}
unsigned char numNetworkedFilters;
if (inBitstream.Read(numNetworkedFilters)==false)
return;
DatabaseFilter networkedFilters[256];
for (i=0; i < numNetworkedFilters; i++)
{
if (networkedFilters[i].Deserialize(&inBitstream)==false)
return;
}
unsigned rowIds[256];
unsigned char numRowIDs;
if (inBitstream.Read(numRowIDs)==false)
return;
for (i=0; i < numRowIDs; i++)
inBitstream.Read(rowIds[i]);
// Convert the safer and more robust networked database filter to the more efficient form the table actually uses.
DataStructures::Table::FilterQuery tableFilters[256];
unsigned numTableFilters=0;
for (i=0; i < numNetworkedFilters; i++)
{
tableFilters[numTableFilters].columnIndex=databaseTable->table.ColumnIndex(networkedFilters[i].columnName);
if (tableFilters[numTableFilters].columnIndex==(unsigned)-1)
continue;
if (networkedFilters[i].columnType!=databaseTable->table.GetColumns()[tableFilters[numTableFilters].columnIndex].columnType)
continue;
tableFilters[numTableFilters].operation=networkedFilters[i].operation;
// It's important that I store a pointer to the class here or the destructor of the class will deallocate the cell twice
tableFilters[numTableFilters++].cellValue=&(networkedFilters[i].cellValue);
}
DataStructures::Table queryResult;
databaseTable->table.QueryTable(columnIndicesSubset, columnIndicesCount, tableFilters, numTableFilters, rowIds, numRowIDs, &queryResult);
outBitstream.Write((MessageID)ID_DATABASE_QUERY_REPLY);
TableSerializer::SerializeTable(&queryResult, &outBitstream);
peer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
}
void LightweightDatabaseServer::OnUpdateRow(RakPeerInterface *peer, Packet *packet)
{
RakNet::BitStream inBitstream(packet->data, packet->length, false);
LightweightDatabaseServer::DatabaseTable *databaseTable = DeserializeClientHeader(&inBitstream, peer, packet, 1);
if (databaseTable==0)
{
printf("ERROR: LightweightDatabaseServer::OnUpdateRow databaseTable==0\n");
return;
}
if (databaseTable->allowRemoteUpdate==false)
{
printf("Warning: LightweightDatabaseServer::OnUpdateRow databaseTable->allowRemoteUpdate==false\n");
return;
}
unsigned char updateMode;
bool hasRowId=false;
unsigned rowId;
unsigned i;
DataStructures::Table::Row *row;
inBitstream.Read(updateMode);
inBitstream.Read(hasRowId);
if (hasRowId)
inBitstream.Read(rowId);
else
rowId=(unsigned) -1; // Not used here but remove the debugging check
unsigned char numCellUpdates;
if (inBitstream.Read(numCellUpdates)==false)
return;
// Read the updates for the row
DatabaseCellUpdate cellUpdates[256];
for (i=0; i < numCellUpdates; i++)
{
if (cellUpdates[i].Deserialize(&inBitstream)==false)
{
printf("ERROR: LightweightDatabaseServer::OnUpdateRow cellUpdates deserialize failed i=%i numCellUpdates=%i\n",i,numCellUpdates);
return;
}
}
if ((RowUpdateMode)updateMode==RUM_UPDATE_EXISTING_ROW)
{
if (hasRowId==false)
{
unsigned rowKey;
row = GetRowFromIP(databaseTable, packet->systemAddress, &rowKey);
if (row==0)
printf("ERROR: LightweightDatabaseServer::OnUpdateRow updateMode==RUM_UPDATE_EXISTING_ROW hasRowId==false");
}
else
{
row = databaseTable->table.GetRowByID(rowId);
if (row==0 || (databaseTable->onlyUpdateOwnRows && RowHasIP(row, packet->systemAddress, databaseTable->SystemAddressColumnIndex)==false))
{
if (row==0)
printf("ERROR: LightweightDatabaseServer::OnUpdateRow row = databaseTable->table.GetRowByID(rowId); row==0\n");
else
printf("ERROR: LightweightDatabaseServer::OnUpdateRow row = databaseTable->table.GetRowByID(rowId); databaseTable->onlyUpdateOwnRows && RowHasIP\n");
return; // You can't update some other system's row
}
}
}
else if ((RowUpdateMode)updateMode==RUM_UPDATE_OR_ADD_ROW)
{
if (hasRowId)
row = databaseTable->table.GetRowByID(rowId);
else
{
unsigned rowKey;
row = GetRowFromIP(databaseTable, packet->systemAddress, &rowKey);
}
if (row==0)
{
row=AddRow(databaseTable, packet->systemAddress, hasRowId, rowId);
if (row==0)
{
printf("ERROR: LightweightDatabaseServer::OnUpdateRow updateMode==RUM_UPDATE_OR_ADD_ROW; row=AddRow; row==0\n");
return;
}
}
else
{
// Existing row
if (databaseTable->onlyUpdateOwnRows && RowHasIP(row, packet->systemAddress, databaseTable->SystemAddressColumnIndex)==false)
{
SystemAddress sysAddr;
memcpy(&sysAddr, row->cells[databaseTable->SystemAddressColumnIndex]->c, sizeof(SystemAddress));
printf("ERROR: LightweightDatabaseServer::OnUpdateRow updateMode==RUM_UPDATE_OR_ADD_ROW; databaseTable->onlyUpdateOwnRows && RowHasIP. packet->systemAddress=%s sysAddr=%s\n",
packet->systemAddress.ToString(true), sysAddr.ToString(true));
return; // You can't update some other system's row
}
}
}
else
{
RakAssert((RowUpdateMode)updateMode==RUM_ADD_NEW_ROW);
row=AddRow(databaseTable, packet->systemAddress, hasRowId, rowId);
if (row==0)
{
printf("ERROR: LightweightDatabaseServer::OnUpdateRow updateMode==RUM_ADD_NEW_ROW; row==0\n");
return;
}
}
unsigned columnIndex;
for (i=0; i < numCellUpdates; i++)
{
columnIndex=databaseTable->table.ColumnIndex(cellUpdates[i].columnName);
RakAssert(columnIndex!=(unsigned)-1); // Unknown column name
if (columnIndex!=(unsigned)-1 && columnIndex!=databaseTable->lastPingResponseColumnIndex && columnIndex!=databaseTable->nextPingSendColumnIndex && columnIndex!=databaseTable->SystemAddressColumnIndex)
{
if (cellUpdates[i].cellValue.isEmpty)
row->cells[columnIndex]->Clear();
else if (cellUpdates[i].columnType==databaseTable->table.GetColumnType(columnIndex))
{
if (cellUpdates[i].columnType==DataStructures::Table::NUMERIC)
{
row->UpdateCell(columnIndex, cellUpdates[i].cellValue.i);
}
else if (cellUpdates[i].columnType==DataStructures::Table::BINARY)
{
row->UpdateCell(columnIndex, cellUpdates[i].cellValue.i, cellUpdates[i].cellValue.c);
}
else
{
RakAssert(cellUpdates[i].columnType==DataStructures::Table::STRING);
row->UpdateCell(columnIndex, cellUpdates[i].cellValue.c);
}
}
}
}
}
void LightweightDatabaseServer::OnRemoveRow(RakPeerInterface *peer, Packet *packet)
{
RakNet::BitStream inBitstream(packet->data, packet->length, false);
LightweightDatabaseServer::DatabaseTable *databaseTable = DeserializeClientHeader(&inBitstream, peer, packet, 0);
if (databaseTable==0)
return;
if (databaseTable->allowRemoteRemove==false)
return;
unsigned rowId;
inBitstream.Read(rowId);
databaseTable->table.RemoveRow(rowId);
}
void LightweightDatabaseServer::OnPong(RakPeerInterface *peer, Packet *packet)
{
(void) peer;
unsigned databaseIndex;
DatabaseTable *databaseTable;
unsigned curIndex;
SystemAddress systemAddress;
RakNetTime time=0;
for (databaseIndex=0; databaseIndex < database.Size(); databaseIndex++)
{
databaseTable=database[databaseIndex];
if (databaseTable->removeRowOnPingFailure)
{
if (time==0)
time=RakNet::GetTime();
DataStructures::BPlusTree<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> &rows = databaseTable->table.GetRows();
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur = rows.GetListHead();
while (cur)
{
for (curIndex=0; curIndex < (unsigned) cur->size; curIndex++)
{
cur->data[curIndex]->cells[databaseTable->SystemAddressColumnIndex]->Get((char*)&systemAddress,0);
if (systemAddress==packet->systemAddress)
{
cur->data[curIndex]->cells[databaseTable->lastPingResponseColumnIndex]->i=time;
}
}
cur=cur->next;
}
}
}
}
LightweightDatabaseServer::DatabaseTable * LightweightDatabaseServer::DeserializeClientHeader(RakNet::BitStream *inBitstream, RakPeerInterface *peer, Packet *packet, int mode)
{
RakNet::BitStream outBitstream;
bool hasPassword=false;
char password[_SIMPLE_DATABASE_PASSWORD_LENGTH];
inBitstream->IgnoreBits(8);
char tableName[_SIMPLE_DATABASE_TABLE_NAME_LENGTH];
stringCompressor->DecodeString(tableName, _SIMPLE_DATABASE_TABLE_NAME_LENGTH, inBitstream);
DatabaseTable *databaseTable = database.Get(tableName);
if (databaseTable==0)
{
outBitstream.Write((MessageID)ID_DATABASE_UNKNOWN_TABLE);
peer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
return 0;
}
const char *dbPass;
if (mode==0)
dbPass=databaseTable->queryPassword;
else if (mode==1)
dbPass=databaseTable->updatePassword;
else
dbPass=databaseTable->removePassword;
inBitstream->Read(hasPassword);
if (hasPassword)
{
if (stringCompressor->DecodeString(password, _SIMPLE_DATABASE_PASSWORD_LENGTH, inBitstream)==false)
return 0;
if (databaseTable->queryPassword[0] && strcmp(password, dbPass)!=0)
{
outBitstream.Write((MessageID)ID_DATABASE_INCORRECT_PASSWORD);
peer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
// Short ban to prevent brute force password attempts
peer->AddToBanList(packet->systemAddress.ToString(false), 1000);
// Don't send a disconnection notification so it closes the connection right away.
peer->CloseConnection(packet->systemAddress, false, 0);
return 0;
}
}
else if (dbPass[0])
{
outBitstream.Write((MessageID)ID_DATABASE_INCORRECT_PASSWORD);
peer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, packet->systemAddress, false);
return 0;
}
return databaseTable;
}
DataStructures::Table::Row * LightweightDatabaseServer::GetRowFromIP(DatabaseTable *databaseTable, SystemAddress systemAddress, unsigned *rowKey)
{
DataStructures::BPlusTree<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> &rows = databaseTable->table.GetRows();
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur = rows.GetListHead();
DataStructures::Table::Row* row;
unsigned i;
while (cur)
{
for (i=0; i < (unsigned)cur->size; i++)
{
row = cur->data[i];
if (RowHasIP(row, systemAddress, databaseTable->SystemAddressColumnIndex ))
{
if (rowKey)
*rowKey=cur->keys[i];
return row;
}
}
cur=cur->next;
}
return 0;
}
bool LightweightDatabaseServer::RowHasIP(DataStructures::Table::Row *row, SystemAddress systemAddress, unsigned SystemAddressColumnIndex)
{
SystemAddress sysAddr;
memcpy(&sysAddr, row->cells[SystemAddressColumnIndex]->c, sizeof(SystemAddress));
return sysAddr==systemAddress;
// Doesn't work in release for some reason
//RakAssert(row->cells[SystemAddressColumnIndex]->isEmpty==false);
//if (memcmp(row->cells[SystemAddressColumnIndex]->c, &systemAddress, sizeof(SystemAddress))==0)
// return true;
// return false;
}
DataStructures::Table::Row * LightweightDatabaseServer::AddRow(LightweightDatabaseServer::DatabaseTable *databaseTable, SystemAddress systemAddress, bool hasRowId, unsigned rowId)
{
DataStructures::Table::Row *row;
if (databaseTable->oneRowPerSystemAddress && GetRowFromIP(databaseTable, systemAddress, 0))
return 0; // This system already has a row.
if (databaseTable->autogenerateRowIDs==false)
{
// For a new row:
// rowID required but not specified OR
// rowId specified but already in the table
// Then exit
if (hasRowId==false || databaseTable->table.GetRowByID(rowId))
return 0;
}
else
rowId=databaseTable->nextRowId++;
// Add new row
row = databaseTable->table.AddRow(rowId);
// Set IP and last update time
if ( databaseTable->oneRowPerSystemAddress || databaseTable->onlyUpdateOwnRows || databaseTable->removeRowOnPingFailure || databaseTable->removeRowOnDisconnect)
row->cells[databaseTable->SystemAddressColumnIndex]->Set((char*)&systemAddress, sizeof(SystemAddress));
if (databaseTable->removeRowOnPingFailure)
{
RakNetTime time = RakNet::GetTime();
row->cells[databaseTable->lastPingResponseColumnIndex]->Set(time);
row->cells[databaseTable->nextPingSendColumnIndex]->Set(time+SEND_PING_INTERVAL);
}
return row;
}
void LightweightDatabaseServer::RemoveRowsFromIP(SystemAddress systemAddress)
{
// Remove rows for tables that do so on a system disconnect
DatabaseTable *databaseTable;
DataStructures::List<unsigned> removeList;
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *cur;
unsigned i,j;
for (i=0; i < database.Size(); i++)
{
databaseTable=database[i];
if (databaseTable->removeRowOnDisconnect)
{
DataStructures::BPlusTree<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> &rows = databaseTable->table.GetRows();
cur = rows.GetListHead();
while (cur)
{
// Mark dropped entities
for (j=0; j < (unsigned)cur->size; j++)
{
if (RowHasIP(cur->data[j], systemAddress, databaseTable->SystemAddressColumnIndex))
removeList.Insert(cur->keys[j]);
}
cur=cur->next;
}
for (j=0; j < removeList.Size(); j++)
databaseTable->table.RemoveRow(removeList[j]);
removeList.Clear(true);
}
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,148 @@
/// \file
/// \brief A simple flat database included with RakNet, useful for a server browser or a lobby server.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __LIGHTWEIGHT_DATABASE_SERVER_H
#define __LIGHTWEIGHT_DATABASE_SERVER_H
#include "Export.h"
#include "PluginInterface.h"
#include "LightweightDatabaseCommon.h"
#include "DS_Map.h"
class RakPeerInterface;
struct Packet;
/// \brief A simple flat database included with RakNet, useful for a server browser or a lobby server.
/// A flat database interface. Adds the ability to track IPs of row updaters and passwords for table read and write operations,
/// Best used for data in which queries which do not need to be updated in real-time
/// \ingroup SIMPLE_DATABSE_GROUP
class RAK_DLL_EXPORT LightweightDatabaseServer : public PluginInterface
{
public:
/// Constructor
LightweightDatabaseServer();
/// Destructor
virtual ~LightweightDatabaseServer();
/// Returns a table by name.
/// It is valid to read and write to the returned pointer.
/// \param[in] tableName The name of the table that was passed to AddTable
/// \return The requested table, or 0 if \a tableName cannot be found.
DataStructures::Table *GetTable(char *tableName);
/// Adds a new table
/// It is valid to read and write to the returned pointer.
/// \param[in] tableName Name of the new table to create. Remote systems will refer to this table by this name.
/// \param[in] allowRemoteQuery true to allow remote systems to query the table. false to not allow this.
/// \param[in] allowRemoteUpdate true to allow remote systems to update rows in the table. false to not allow this.
/// \param[in] allowRemoteRemove true to allow remote systems to remove rows from the table. false to not allow this.
/// \param[in] queryPassword The password required to query the table. Pass an empty string for no password.
/// \param[in] updatePassword The password required to update rows in the table. Pass an empty string for no password.
/// \param[in] removePassword The password required to remove rows from the table. Pass an empty string for no password.
/// \param[in] oneRowPerSystemAddress Only used if allowRemoteUpdate==true. This limits remote systems to one row.
/// \param[in] onlyUpdateOwnRows Only used if allowRemoteUpdate==true. This limits remote systems to only updating rows they created.
/// \param[in] removeRowOnPingFailure Only used if allowRemoteUpdate==true and removeRowOnDisconnect==false. If true, this will periodically ping disconnected systems and remove rows created by that system if that system does not respond to pings.
/// \param[in] removeRowOnDisconnect Only used if allowRemoteUpdate==true. This removes rows created by a system when that system disconnects.
/// \param[in] autogenerateRowIDs true to automatically generate row IDs. Rows are stored in order by row ID. If false, the clients must specify a unique row ID when adding rows. If they specify a row that already exists the addition is ignored.
/// \return The newly created table, or 0 on failure.
DataStructures::Table* AddTable(char *tableName,
bool allowRemoteQuery,
bool allowRemoteUpdate,
bool allowRemoteRemove,
const char *queryPassword,
const char *updatePassword,
const char *removePassword,
bool oneRowPerSystemAddress,
bool onlyUpdateOwnRows,
bool removeRowOnPingFailure,
bool removeRowOnDisconnect,
bool autogenerateRowIDs);
/// Removes a table by name.
/// \param[in] tableName The name of the table that was passed to AddTable
/// \return true on success, false on failure.
bool RemoveTable(char *tableName);
/// Clears all memory.
void Clear(void);
// Gets the next valid auto-generated rowId for a table and increments it.
unsigned GetAndIncrementRowID(char *tableName);
/// Returns a linked list of ordered lists containing the rows of a table, by name.
/// The returned structure is internal to the BPlus tree. See DS_BPlusTree
/// This is a convenience accessor, as you can also get this from the table returned from GetTable()
/// \param[in] tableName The name of the table that was passed to AddTable
/// \return The requested rows, or 0 if \a tableName cannot be found.
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> *GetTableRows(char *tableName);
/// \internal For plugin handling
virtual void OnAttach(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void Update(RakPeerInterface *peer);
/// \internal For plugin handling
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal For plugin handling
virtual void OnShutdown(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress);
struct DatabaseTable
{
bool allowRemoteUpdate;
bool allowRemoteQuery;
bool allowRemoteRemove;
char updatePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH];
char queryPassword[_SIMPLE_DATABASE_PASSWORD_LENGTH];
char removePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH];
bool oneRowPerSystemAddress;
bool onlyUpdateOwnRows;
bool removeRowOnPingFailure;
bool removeRowOnDisconnect;
bool autogenerateRowIDs;
char tableName[_SIMPLE_DATABASE_TABLE_NAME_LENGTH];
// Used if autogenerateRowIDs==true
unsigned nextRowId;
unsigned SystemAddressColumnIndex;
unsigned lastPingResponseColumnIndex;
unsigned nextPingSendColumnIndex;
RakNetTime nextRowPingCheck;
DataStructures::Table table;
};
static int DatabaseTableComp( char* const &key1, char* const &key2 );
protected:
DataStructures::Map<char *, LightweightDatabaseServer::DatabaseTable*, LightweightDatabaseServer::DatabaseTableComp> database;
void OnQueryRequest(RakPeerInterface *peer, Packet *packet);
void OnUpdateRow(RakPeerInterface *peer, Packet *packet);
void OnRemoveRow(RakPeerInterface *peer, Packet *packet);
void OnPong(RakPeerInterface *peer, Packet *packet);
// mode 0 = query, mode 1 = update, mode 2 = remove
DatabaseTable * DeserializeClientHeader(RakNet::BitStream *inBitstream, RakPeerInterface *peer, Packet *packet, int mode);
DataStructures::Table::Row * GetRowFromIP(DatabaseTable *databaseTable, SystemAddress systemAddress, unsigned *rowId);
bool RowHasIP(DataStructures::Table::Row *row, SystemAddress systemAddress, unsigned SystemAddressColumnIndex);
DataStructures::Table::Row * AddRow(LightweightDatabaseServer::DatabaseTable *databaseTable, SystemAddress systemAddress, bool hasRowId, unsigned rowId);
void RemoveRowsFromIP(SystemAddress systemAddress);
};
#endif

View File

@@ -0,0 +1,12 @@
#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(__WIN32)
#include <string.h>
int _stricmp(const char* s1, const char* s2)
{
return strcasecmp(s1,s2);
}
int _strnicmp(const char* s1, const char* s2, size_t n)
{
return strncasecmp(s1,s2,n);
}
#endif

14
thirdparty/raknet/Source/LinuxStrings.h vendored Normal file
View File

@@ -0,0 +1,14 @@
#ifndef _GCC_WIN_STRINGS
#define _GCC_WIN_STRINGS
#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_WIN32)
int _stricmp(const char* s1, const char* s2);
int _strnicmp(const char* s1, const char* s2, size_t n);
#ifndef _vsnprintf
#define _vsnprintf vsnprintf
#endif
#endif
#endif

View File

@@ -0,0 +1,268 @@
#include "LogCommandParser.h"
#include "TransportInterface.h"
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include "LinuxStrings.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
LogCommandParser::LogCommandParser()
{
RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[<ChannelName>] - Subscribes to a named channel, or all channels");
RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[<ChannelName>] - Unsubscribes from a named channel, or all channels");
memset(channelNames,0,sizeof(channelNames));
}
LogCommandParser::~LogCommandParser()
{
}
bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString)
{
(void) originalString;
if (strcmp(command, "Subscribe")==0)
{
unsigned channelIndex;
if (numParameters==0)
{
Subscribe(systemAddress, 0);
transport->Send(systemAddress, "Subscribed to all channels.\r\n");
}
else if (numParameters==1)
{
if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1)
{
transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]);
}
else
{
transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]);
PrintChannels(systemAddress, transport);
}
}
else
{
transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n");
}
}
else if (strcmp(command, "Unsubscribe")==0)
{
unsigned channelIndex;
if (numParameters==0)
{
Unsubscribe(systemAddress, 0);
transport->Send(systemAddress, "Unsubscribed from all channels.\r\n");
}
else if (numParameters==1)
{
if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1)
{
transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]);
}
else
{
transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]);
PrintChannels(systemAddress, transport);
}
}
else
{
transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n");
}
}
return true;
}
const char *LogCommandParser::GetName(void) const
{
return "Logger";
}
void LogCommandParser::SendHelp(TransportInterface *transport, SystemAddress systemAddress)
{
transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n");
transport->Send(systemAddress, "Each log is associated with a named channel.\r\n");
transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n");
PrintChannels(systemAddress, transport);
}
void LogCommandParser::AddChannel(const char *channelName)
{
unsigned channelIndex;
channelIndex = GetChannelIndexFromName(channelName);
// Each channel can only be added once.
assert(channelIndex==(unsigned)-1);
unsigned i;
for (i=0; i < 32; i++)
{
if (channelNames[i]==0)
{
// Assuming a persistent static string.
channelNames[i]=channelName;
return;
}
}
// No more available channels - max 32 with this implementation where I save subscribed channels with bit operations
assert(0);
}
void LogCommandParser::WriteLog(const char *channelName, const char *format, ...)
{
if (channelName==0 || format==0)
return;
unsigned channelIndex;
channelIndex = GetChannelIndexFromName(channelName);
if (channelIndex==(unsigned)-1)
{
AddChannel(channelName);
}
char text[REMOTE_MAX_TEXT_INPUT];
va_list ap;
va_start(ap, format);
_vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap);
va_end(ap);
text[REMOTE_MAX_TEXT_INPUT-1]=0;
// Make sure that text ends in \r\n
int textLen;
textLen=(int)strlen(text);
if (textLen==0)
return;
if (text[textLen-1]=='\n')
{
text[textLen-1]=0;
}
if (textLen < REMOTE_MAX_TEXT_INPUT-4)
strcat(text, "\r\n");
else
{
text[textLen-3]='\r';
text[textLen-2]='\n';
text[textLen-1]=0;
}
// For each user that subscribes to this channel, send to them.
unsigned i;
for (i=0; i < remoteUsers.Size(); i++)
{
if (remoteUsers[i].channels & (1 << channelIndex))
{
trans->Send(remoteUsers[i].systemAddress, text);
}
}
}
void LogCommandParser::PrintChannels(SystemAddress systemAddress, TransportInterface *transport) const
{
unsigned i;
bool anyChannels=false;
transport->Send(systemAddress, "CHANNELS:\r\n");
for (i=0; i < 32; i++)
{
if (channelNames[i])
{
transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]);
anyChannels=true;
}
}
if (anyChannels==false)
transport->Send(systemAddress, "None.\r\n");
}
void LogCommandParser::OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport)
{
(void) systemAddress;
(void) transport;
}
void LogCommandParser::OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport)
{
(void) transport;
Unsubscribe(systemAddress, 0);
}
unsigned LogCommandParser::Unsubscribe(SystemAddress systemAddress, const char *channelName)
{
unsigned i;
for (i=0; i < remoteUsers.Size(); i++)
{
if (remoteUsers[i].systemAddress==systemAddress)
{
if (channelName==0)
{
// Unsubscribe from all and delete this user.
remoteUsers[i]=remoteUsers[remoteUsers.Size()-1];
remoteUsers.RemoveFromEnd();
return 0;
}
else
{
unsigned channelIndex;
channelIndex = GetChannelIndexFromName(channelName);
if (channelIndex!=(unsigned)-1)
{
remoteUsers[i].channels&=0xFFFF ^ (1<<channelIndex); // Unset this bit
}
return channelIndex;
}
}
}
return (unsigned)-1;
}
unsigned LogCommandParser::Subscribe(SystemAddress systemAddress, const char *channelName)
{
unsigned i;
unsigned channelIndex=(unsigned)-1;
if (channelName)
{
channelIndex = GetChannelIndexFromName(channelName);
if (channelIndex==(unsigned)-1)
return channelIndex;
}
for (i=0; i < remoteUsers.Size(); i++)
{
if (remoteUsers[i].systemAddress==systemAddress)
{
if (channelName)
remoteUsers[i].channels|=1<<channelIndex; // Set this bit for an existing user
else
remoteUsers[i].channels=0xFFFF;
return channelIndex;
}
}
// Make a new user
SystemAddressAndChannel newUser;
newUser.systemAddress = systemAddress;
if (channelName)
newUser.channels=1<<channelIndex;
else
newUser.channels=0xFFFF;
remoteUsers.Insert(newUser);
return channelIndex;
}
unsigned LogCommandParser::GetChannelIndexFromName(const char *channelName)
{
unsigned i;
for (i=0; i < 32; i++)
{
if (channelNames[i]==0)
return (unsigned) -1;
if (_stricmp(channelNames[i], channelName)==0)
return i;
}
return (unsigned)-1;
}
void LogCommandParser::OnTransportChange(TransportInterface *transport)
{
// I don't want users to have to pass TransportInterface *transport to Log.
trans=transport;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@@ -0,0 +1,117 @@
/// \file
/// \brief Contains LogCommandParser , Used to send logs to connected consoles
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __LOG_COMMAND_PARSER
#define __LOG_COMMAND_PARSER
class RakPeerInterface;
#include "CommandParserInterface.h"
#include "Export.h"
/// \brief Adds the ability to send logging output to a remote console
class RAK_DLL_EXPORT LogCommandParser : public CommandParserInterface
{
public:
LogCommandParser();
~LogCommandParser();
/// Given \a command with parameters \a parameterList , do whatever processing you wish.
/// \param[in] command The command to process
/// \param[in] numParameters How many parameters were passed along with the command
/// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on.
/// \param[in] transport The transport interface we can use to write to
/// \param[in] systemAddress The player that sent this command.
/// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing
bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString);
/// You are responsible for overriding this function and returning a static string, which will identifier your parser.
/// This should return a static string
/// \return The name that you return.
const char *GetName(void) const;
/// A callback for when you are expected to send a brief description of your parser to \a systemAddress
/// \param[in] transport The transport interface we can use to write to
/// \param[in] systemAddress The player that requested help.
void SendHelp(TransportInterface *transport, SystemAddress systemAddress);
/// All logs must be associated with a channel. This is a filter so that remote clients only get logs for a system they care about.
// If you call Log with a channel that is unknown, that channel will automatically be added
/// \param[in] channelName A persistent string naming the channel. Don't deallocate this string.
void AddChannel(const char *channelName);
/// Write a log to a channel.
/// Logs are not buffered, so only remote consoles connected and subscribing at the time you write will get the output.
/// \param[in] format Same as printf()
/// \param[in] ... Same as printf()
void WriteLog(const char *channelName, const char *format, ...);
/// A callback for when \a systemAddress has connected to us.
/// \param[in] systemAddress The player that has connected.
/// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players.
void OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport);
/// A callback for when \a systemAddress has disconnected, either gracefully or forcefully
/// \param[in] systemAddress The player that has disconnected.
/// \param[in] transport The transport interface that sent us this information.
void OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport);
/// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer
/// This is the place to do it
/// \param[in] transport The new TransportInterface
void OnTransportChange(TransportInterface *transport);
protected:
/// Sends the currently active channels to the user
/// \param[in] systemAddress The player to send to
/// \param[in] transport The transport interface to use to send the channels
void PrintChannels(SystemAddress systemAddress, TransportInterface *transport) const;
/// Unsubscribe a user from a channel (or from all channels)
/// \param[in] systemAddress The player to unsubscribe to
/// \param[in] channelName If 0, then unsubscribe from all channels. Otherwise unsubscribe from the named channel
unsigned Unsubscribe(SystemAddress systemAddress, const char *channelName);
/// Subscribe a user to a channel (or to all channels)
/// \param[in] systemAddress The player to subscribe to
/// \param[in] channelName If 0, then subscribe from all channels. Otherwise subscribe to the named channel
unsigned Subscribe(SystemAddress systemAddress, const char *channelName);
/// Given the name of a channel, return the index into channelNames where it is located
/// \param[in] channelName The name of the channel
unsigned GetChannelIndexFromName(const char *channelName);
/// One of these structures is created per player
struct SystemAddressAndChannel
{
/// The ID of the player
SystemAddress systemAddress;
/// Bitwise representations of the channels subscribed to. If bit 0 is set, then we subscribe to channelNames[0] and so on.
unsigned channels;
};
/// The list of remote users. Added to when users subscribe, removed when they disconnect or unsubscribe
DataStructures::List<SystemAddressAndChannel> remoteUsers;
/// Names of the channels at each bit, or 0 for an unused channel
const char *channelNames[32];
/// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log
TransportInterface *trans;
};
#endif

43
thirdparty/raknet/Source/MTUSize.h vendored Normal file
View File

@@ -0,0 +1,43 @@
/// \file
/// \brief \b [Internal] Defines the default maximum transfer unit.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef DEFAULT_MTU_SIZE
/// The MTU size to use if RakPeer::SetMTUSize() is not called.
/// \remarks I think many people forget to call RakPeer::SetMTUSize() so I'm setting this to 1500 by default for efficiency.
/// \li \em 17914 16 Mbit/Sec Token Ring
/// \li \em 4464 4 Mbits/Sec Token Ring
/// \li \em 4352 FDDI
/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches.
/// \li \em 1492. The size PPPoE prefers.
/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.)
/// \li \em 1468. The size DHCP prefers.
/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc.
/// \li \em 1430. The size VPN and PPTP prefer.
/// \li \em 1400. Maximum size for AOL DSL.
/// \li \em 576. Typical value to connect to dial-up ISPs.
#ifdef _XBOX360
#define DEFAULT_MTU_SIZE 1264
#else
#define DEFAULT_MTU_SIZE 1492
#endif
/// The largest value for an UDP datagram
/// \sa RakPeer::SetMTUSize()
#define MAXIMUM_MTU_SIZE 1492
#endif

194
thirdparty/raknet/Source/Makefile.am vendored Normal file
View File

@@ -0,0 +1,194 @@
lib_LTLIBRARIES = libraknet.la
libraknet_la_LDFLAGS = -version-info 0:0:0 $(PTHREAD_LIBS) $(PTHREAD_CFLAGS)
libraknet_la_CPPFLAGS = $(PTHREAD_CFLAGS)
libraknet_la_SOURCES = AsynchronousFileIO.cpp\
AutoRPC.cpp\
BitStream.cpp\
CheckSum.cpp\
CommandParserInterface.cpp\
ConnectionGraph.cpp\
ConsoleServer.cpp\
DataBlockEncryptor.cpp\
DataCompressor.cpp\
DirectoryDeltaTransfer.cpp\
DS_BytePool.cpp\
DS_ByteQueue.cpp\
DS_HuffmanEncodingTree.cpp\
DS_Table.cpp\
EmailSender.cpp\
EncodeClassName.cpp\
EpochTimeToString.cpp\
ExtendedOverlappedPool.cpp\
FileList.cpp\
FileListTransfer.cpp\
FileOperations.cpp\
FormatString.cpp\
FullyConnectedMesh.cpp\
FunctionThread.cpp\
Gen_RPC8.cpp\
GetTime.cpp\
GridSectorizer.cpp\
LightweightDatabaseClient.cpp\
LightweightDatabaseCommon.cpp\
LightweightDatabaseServer.cpp\
LinuxStrings.cpp\
LogCommandParser.cpp\
MessageFilter.cpp\
NatPunchthrough.cpp\
NetworkIDManager.cpp\
NetworkIDObject.cpp\
PacketConsoleLogger.cpp\
PacketFileLogger.cpp\
PacketLogger.cpp\
PluginInterface.cpp\
RakMemoryOverride.cpp\
RakNetCommandParser.cpp\
RakNetStatistics.cpp\
RakNetTransport.cpp\
RakNetTypes.cpp\
RakNetworkFactory.cpp\
RakPeer.cpp\
RakSleep.cpp\
RakString.cpp\
RakThread.cpp\
Rand.cpp\
ReadyEvent.cpp\
ReliabilityLayer.cpp\
ReplicaManager.cpp\
ReplicaManager2.cpp\
rijndael.cpp\
Router.cpp\
RPCMap.cpp\
SHA1.cpp\
SimpleMutex.cpp\
SocketLayer.cpp\
StringCompressor.cpp\
StringTable.cpp\
SuperFastHash.cpp\
SystemAddressList.cpp\
TableSerializer.cpp\
TCPInterface.cpp\
TelnetTransport.cpp\
ThreadsafePacketLogger.cpp\
_FindFirst.cpp\
Itoa.cpp\
HTTPConnection.cpp
pkginclude_HEADERS = AsynchronousFileIO.h\
AutoRPC.h\
AutopatcherPatchContext.h\
AutopatcherRepositoryInterface.h\
BigTypes.h\
BitStream.h\
CheckSum.h\
ClientContextStruct.h\
CommandParserInterface.h\
ConnectionGraph.h\
ConsoleServer.h\
DataBlockEncryptor.h\
DataCompressor.h\
DirectoryDeltaTransfer.h\
DS_BinarySearchTree.h\
DS_BPlusTree.h\
DS_BytePool.h\
DS_ByteQueue.h\
DS_Heap.h\
DS_HuffmanEncodingTree.h\
DS_HuffmanEncodingTreeFactory.h\
DS_HuffmanEncodingTreeNode.h\
DS_LinkedList.h\
DS_List.h\
DS_Map.h\
DS_MemoryPool.h\
DS_OrderedChannelHeap.h\
DS_OrderedList.h\
DS_Queue.h\
DS_QueueLinkedList.h\
DS_RangeList.h\
DS_Table.h\
DS_Tree.h\
DS_WeightedGraph.h\
EmailSender.h\
EpochTimeToString.h\
Export.h\
ExtendedOverlappedPool.h\
FileList.h\
FileListTransfer.h\
FileListTransferCBInterface.h\
FileOperations.h\
FormatString.h\
FullyConnectedMesh.h\
FunctionThread.h\
Gen_RPC8.h\
GetTime.h\
GridSectorizer.h\
InternalPacket.h\
Kbhit.h\
LightweightDatabaseClient.h\
LightweightDatabaseCommon.h\
LightweightDatabaseServer.h\
LinuxStrings.h\
LogCommandParser.h\
MessageFilter.h\
MessageIdentifiers.h\
MTUSize.h\
NatPunchthrough.h\
NetworkIDManager.h\
NetworkIDObject.h\
PacketConsoleLogger.h\
PacketFileLogger.h\
PacketLogger.h\
PacketPool.h\
PacketPriority.h\
PluginInterface.h\
RakAssert.h\
RakMemoryOverride.h\
RakNetCommandParser.h\
RakNetDefines.h\
RakNetStatistics.h\
RakNetTransport.h\
RakNetTypes.h\
RakNetVersion.h\
RakNetworkFactory.h\
RakPeer.h\
RakPeerInterface.h\
RakSleep.h\
RakString.h\
RakThread.h\
Rand.h\
ReadyEvent.h\
RefCountedObj.h\
ReliabilityLayer.h\
Replica.h\
ReplicaEnums.h\
ReplicaManager.h\
ReplicaManager2.h\
Rijndael-Boxes.h\
Rijndael.h\
Router.h\
RouterInterface.h\
RPCMap.h\
RPCNode.h\
RSACrypt.h\
SHA1.h\
SimpleMutex.h\
SimpleTCPServer.h\
SingleProducerConsumer.h\
SocketLayer.h\
StringCompressor.h\
StringTable.h\
SuperFastHash.h\
SystemAddressList.h\
TableSerializer.h\
TCPInterface.h\
TelnetTransport.h\
ThreadPool.h\
ThreadsafePacketLogger.h\
TransportInterface.h\
Types.h\
_FindFirst.h\
Itoa.h\
HTTPConnection.h
EXTRA_DIST = RakNet.vcproj RakNet3.0.vcproj RakNet3.0.vcproj.user RakNet_vc8.vcproj

View File

@@ -0,0 +1,363 @@
#include "MessageFilter.h"
#include "RakAssert.h"
#include "GetTime.h"
#include "MessageIdentifiers.h"
#include "RakPeerInterface.h"
#include "RakAssert.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
int MessageFilterStrComp( char *const &key,char *const &data )
{
return strcmp(key,data);
}
int FilteredSystemComp( const SystemAddress &key, const FilteredSystem &data )
{
if (key < data.systemAddress)
return -1;
else if (key==data.systemAddress)
return 0;
else
return 1;
}
int FilterSetComp( const int &key, FilterSet * const &data )
{
if (key < data->filterSetID)
return -1;
else if (key==data->filterSetID)
return 0;
else
return 1;
}
MessageFilter::MessageFilter()
{
}
MessageFilter::~MessageFilter()
{
Clear();
}
void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID)
{
autoAddNewConnectionsToFilter=filterSetID;
}
void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID)
{
RakAssert(messageIDStart <= messageIDEnd);
FilterSet *filterSet = GetFilterSetByID(filterSetID);
int i;
for (i=messageIDStart; i <= messageIDEnd; ++i)
filterSet->allowedIDs[i]=allow;
}
void MessageFilter::SetAllowRPC(bool allow, const char *functionName, int filterSetID)
{
(void) allow;
FilterSet *filterSet = GetFilterSetByID(filterSetID);
bool objectExists;
unsigned index = filterSet->allowedRPCs.GetIndexFromKey((char *const) functionName, &objectExists);
if (objectExists==false)
{
char *str = (char*) rakMalloc( strlen(functionName)+1 );
strcpy(str, functionName);
filterSet->allowedRPCs.InsertAtIndex(str, index);
}
}
void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNetTime banTimeMS, int filterSetID)
{
FilterSet *filterSet = GetFilterSetByID(filterSetID);
filterSet->kickOnDisallowedMessage=kickOnDisallowed;
filterSet->disallowedMessageBanTimeMS=banTimeMS;
filterSet->banOnDisallowedMessage=banOnDisallowed;
}
void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData, unsigned char messageID))
{
FilterSet *filterSet = GetFilterSetByID(filterSetID);
filterSet->invalidMessageCallback=invalidMessageCallback;
filterSet->disallowedCallbackUserData=userData;
}
void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData))
{
FilterSet *filterSet = GetFilterSetByID(filterSetID);
filterSet->timeoutCallback=invalidMessageCallback;
filterSet->timeoutUserData=userData;
}
void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNetTime banTimeMS, int filterSetID)
{
FilterSet *filterSet = GetFilterSetByID(filterSetID);
filterSet->maxMemberTimeMS=allowedTimeMS;
filterSet->banOnFilterTimeExceed=banOnExceed;
filterSet->timeExceedBanTimeMS=banTimeMS;
}
int MessageFilter::GetSystemFilterSet(SystemAddress systemAddress)
{
bool objectExists;
unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists);
if (objectExists==false)
return -1;
else
return systemList[index].filter->filterSetID;
}
void MessageFilter::SetSystemFilterSet(SystemAddress systemAddress, int filterSetID)
{
// Allocate this filter set if it doesn't exist.
RakAssert(systemAddress!=UNASSIGNED_SYSTEM_ADDRESS);
bool objectExists;
unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists);
if (objectExists==false)
{
if (filterSetID<0)
return;
FilteredSystem filteredSystem;
filteredSystem.filter = GetFilterSetByID(filterSetID);
filteredSystem.systemAddress=systemAddress;
filteredSystem.timeEnteredThisSet=RakNet::GetTime();
systemList.Insert(systemAddress, filteredSystem, true);
}
else
{
if (filterSetID>=0)
{
FilterSet *filterSet = GetFilterSetByID(filterSetID);
systemList[index].timeEnteredThisSet=RakNet::GetTime();
systemList[index].filter=filterSet;
}
else
{
systemList.RemoveAtIndex(index);
}
}
}
unsigned MessageFilter::GetSystemCount(int filterSetID) const
{
if (filterSetID==-1)
return systemList.Size();
else
{
unsigned i;
unsigned count=0;
for (i=0; i < systemList.Size(); i++)
if (systemList[i].filter->filterSetID==filterSetID)
++count;
return count;
}
}
SystemAddress MessageFilter::GetSystemByIndex(int filterSetID, unsigned index)
{
if (filterSetID==-1)
return systemList[index].systemAddress;
else
{
unsigned i;
unsigned count=0;
for (i=0; i < systemList.Size(); i++)
{
if (systemList[i].filter->filterSetID==filterSetID)
{
if (index==count)
return systemList[i].systemAddress;
count++;
}
}
}
return UNASSIGNED_SYSTEM_ADDRESS;
}
unsigned MessageFilter::GetFilterSetCount(void) const
{
return filterList.Size();
}
int MessageFilter::GetFilterSetIDByIndex(unsigned index)
{
return filterList[index]->filterSetID;
}
void MessageFilter::DeleteFilterSet(int filterSetID)
{
FilterSet *filterSet;
bool objectExists;
unsigned i,index;
index = filterList.GetIndexFromKey(filterSetID, &objectExists);
if (objectExists)
{
filterSet=filterList[index];
DeallocateFilterSet(filterSet);
filterList.RemoveAtIndex(index);
// Don't reference this pointer any longer
i=0;
while (i < systemList.Size())
{
if (systemList[i].filter==filterSet)
systemList.RemoveAtIndex(i);
else
++i;
}
}
}
void MessageFilter::Clear(void)
{
unsigned i;
systemList.Clear();
for (i=0; i < filterList.Size(); i++)
DeallocateFilterSet(filterList[i]);
filterList.Clear();
}
void MessageFilter::DeallocateFilterSet(FilterSet* filterSet)
{
unsigned i;
for (i=0; i < filterSet->allowedRPCs.Size(); i++)
rakFree(filterSet->allowedRPCs[i]);
delete filterSet;
}
FilterSet* MessageFilter::GetFilterSetByID(int filterSetID)
{
RakAssert(filterSetID>=0);
bool objectExists;
unsigned index;
index = filterList.GetIndexFromKey(filterSetID, &objectExists);
if (objectExists)
return filterList[index];
else
{
FilterSet *newFilterSet = new FilterSet;
memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool));
newFilterSet->banOnFilterTimeExceed=false;
newFilterSet->kickOnDisallowedMessage=false;
newFilterSet->banOnDisallowedMessage=false;
newFilterSet->disallowedMessageBanTimeMS=0;
newFilterSet->timeExceedBanTimeMS=0;
newFilterSet->maxMemberTimeMS=0;
newFilterSet->filterSetID=filterSetID;
newFilterSet->invalidMessageCallback=0;
newFilterSet->timeoutCallback=0;
newFilterSet->timeoutUserData=0;
filterList.Insert(filterSetID, newFilterSet, true);
return newFilterSet;
}
}
void MessageFilter::OnAttach(RakPeerInterface *peer)
{
(void) peer;
}
void MessageFilter::OnDetach(RakPeerInterface *peer)
{
(void) peer;
}
void MessageFilter::OnShutdown(RakPeerInterface *peer)
{
(void) peer;
}
void MessageFilter::OnInvalidMessage(RakPeerInterface *peer, FilterSet *filterSet, SystemAddress systemAddress, unsigned char messageID)
{
if (filterSet->invalidMessageCallback)
filterSet->invalidMessageCallback(peer, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID);
if (filterSet->banOnDisallowedMessage)
peer->AddToBanList(systemAddress.ToString(false), filterSet->disallowedMessageBanTimeMS);
if (filterSet->kickOnDisallowedMessage)
peer->CloseConnection(systemAddress, true, 0);
}
void MessageFilter::Update(RakPeerInterface *peer)
{
// Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action.
RakNetTime time = RakNet::GetTime();
unsigned index;
index=0;
while (index < systemList.Size())
{
if (systemList[index].filter &&
systemList[index].filter->maxMemberTimeMS>0 &&
time-systemList[index].timeEnteredThisSet >= systemList[index].filter->maxMemberTimeMS)
{
if (systemList[index].filter->timeoutCallback)
systemList[index].filter->timeoutCallback(peer, systemList[index].systemAddress, systemList[index].filter->filterSetID, systemList[index].filter->timeoutUserData);
if (systemList[index].filter->banOnFilterTimeExceed)
peer->AddToBanList(systemList[index].systemAddress.ToString(false), systemList[index].filter->timeExceedBanTimeMS);
peer->CloseConnection(systemList[index].systemAddress, true, 0);
systemList.RemoveAtIndex(index);
}
else
++index;
}
}
PluginReceiveResult MessageFilter::OnReceive(RakPeerInterface *peer, Packet *packet)
{
bool objectExists;
unsigned index;
unsigned char messageId;
switch (packet->data[0])
{
case ID_CONNECTION_LOST:
case ID_DISCONNECTION_NOTIFICATION:
// Lost system, remove from the list
systemList.RemoveIfExists(packet->systemAddress);
break;
case ID_NEW_INCOMING_CONNECTION:
case ID_CONNECTION_REQUEST_ACCEPTED:
// New system, automatically assign to filter set if appropriate
if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(packet->systemAddress)==false)
SetSystemFilterSet(packet->systemAddress, autoAddNewConnectionsToFilter);
break;
case ID_CONNECTION_ATTEMPT_FAILED:
case ID_NO_FREE_INCOMING_CONNECTIONS:
case ID_RSA_PUBLIC_KEY_MISMATCH:
case ID_CONNECTION_BANNED:
case ID_INVALID_PASSWORD:
case ID_MODIFIED_PACKET:
case ID_PONG:
case ID_ALREADY_CONNECTED:
case ID_ADVERTISE_SYSTEM:
case ID_REMOTE_DISCONNECTION_NOTIFICATION:
case ID_REMOTE_CONNECTION_LOST:
case ID_REMOTE_NEW_INCOMING_CONNECTION:
case ID_DOWNLOAD_PROGRESS:
break;
default:
if (packet->data[0]==ID_TIMESTAMP)
{
if (packet->length<sizeof(MessageID) + sizeof(RakNetTime))
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Invalid message
messageId=packet->data[sizeof(MessageID) + sizeof(RakNetTime)];
}
else
messageId=packet->data[0];
// If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE
index = systemList.GetIndexFromKey(packet->systemAddress, &objectExists);
if (objectExists==false)
break;
if (messageId==ID_RPC)
{
const char *uniqueIdentifier = peer->GetRPCString((const char*) packet->data, packet->bitSize, packet->systemAddress);
if (systemList[index].filter->allowedRPCs.HasData((char *const)uniqueIdentifier)==false)
{
OnInvalidMessage(peer, systemList[index].filter, packet->systemAddress, packet->data[0]);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
}
else
{
if (systemList[index].filter->allowedIDs[messageId]==false)
{
OnInvalidMessage(peer, systemList[index].filter, packet->systemAddress, packet->data[0]);
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
}
break;
}
return RR_CONTINUE_PROCESSING;
}
void MessageFilter::OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress)
{
(void) peer;
(void) systemAddress;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

193
thirdparty/raknet/Source/MessageFilter.h vendored Normal file
View File

@@ -0,0 +1,193 @@
/// \file
/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __MESSAGE_FILTER_PLUGIN_H
#define __MESSAGE_FILTER_PLUGIN_H
class RakPeerInterface;
#include "RakNetTypes.h"
#include "PluginInterface.h"
#include "DS_OrderedList.h"
#include "Export.h"
/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do.
#define MESSAGE_FILTER_MAX_MESSAGE_ID 256
/// \internal Has to be public so some of the shittier compilers can use it.
int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data );
/// \internal Has to be public so some of the shittier compilers can use it.
struct FilterSet
{
bool banOnFilterTimeExceed;
bool kickOnDisallowedMessage;
bool banOnDisallowedMessage;
RakNetTime disallowedMessageBanTimeMS;
RakNetTime timeExceedBanTimeMS;
RakNetTime maxMemberTimeMS;
void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData, unsigned char messageID);
void *disallowedCallbackUserData;
void (*timeoutCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData);
void *timeoutUserData;
int filterSetID;
bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID];
DataStructures::OrderedList<char *, char *, MessageFilterStrComp> allowedRPCs;
};
/// \internal Has to be public so some of the shittier compilers can use it.
int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data );
/// \internal Has to be public so some of the shittier compilers can use it.
struct FilteredSystem
{
SystemAddress systemAddress;
FilterSet *filter;
RakNetTime timeEnteredThisSet;
};
/// \internal Has to be public so some of the shittier compilers can use it.
int RAK_DLL_EXPORT FilteredSystemComp( const SystemAddress &key, const FilteredSystem &data );
/// \defgroup MESSAGEFILTER_GROUP MessageFilter
/// \ingroup PLUGINS_GROUP
/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed.
/// The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.
/// You implicitly define FilterSets, and add allowed message IDs and RPC calls to these FilterSets.
/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.
/// You can automatically assign systems to a filter.
/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.
/// Each system is a member of either zero or one filters.
/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other).
/// \ingroup MESSAGEFILTER_GROUP
class RAK_DLL_EXPORT MessageFilter : public PluginInterface
{
public:
MessageFilter();
virtual ~MessageFilter();
// --------------------------------------------------------------------------------------------
// User functions
// --------------------------------------------------------------------------------------------
/// Automatically add all new systems to a particular filter
/// Defaults to -1
/// \param[in] filterSetID Which filter to add new systems to. <0 for do not add.
void SetAutoAddNewConnectionsToFilter(int filterSetID);
/// Allow a range of message IDs
/// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS
/// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function.
/// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins!
/// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive.
/// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive.
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID);
/// Allow an RPC function, by name
/// \param[in] allow True to allow an RPC call with this function name, false to disallow. All RPCs are disabled by default.
/// \param[in] functionName the function name of the RPC call. Must match the function name exactly, including case.
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
void SetAllowRPC(bool allow, const char *functionName, int filterSetID);
/// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time
/// By default no action is taken. The message is simply ignored.
/// param[in] 0 for permanent ban, >0 for ban time in milliseconds.
/// \param[in] kickOnDisallowed kick the system that sent a disallowed message.
/// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration
/// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList.
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNetTime banTimeMS, int filterSetID);
/// Set a user callback to be called on an invalid message for a particular filterSet
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
/// \param[in] userData A pointer passed with the callback
/// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters.
void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData, unsigned char messageID));
/// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
/// \param[in] userData A pointer passed with the callback
/// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters.
void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData));
/// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned.
/// By default there is no limit to how long a connection can stay in a particular filter set.
/// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set.
/// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded
/// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList.
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings.
void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNetTime banTimeMS, int filterSetID);
/// Get the filterSetID a system is using. Returns -1 for none.
/// \param[in] systemAddress The system we are referring to
int GetSystemFilterSet(SystemAddress systemAddress);
/// Assign a system to a filter set.
/// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter()
/// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets.
/// \param[in] systemAddress The system we are referring to
/// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets.
void SetSystemFilterSet(SystemAddress systemAddress, int filterSetID);
/// Returns the number of systems subscribed to a particular filter set
/// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value.
/// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case).
unsigned GetSystemCount(int filterSetID) const;
/// Returns a system subscribed to a particular filter set,by index.
/// index should be between 0 and the GetSystemCount(filterSetID)-1;
/// \param[in] filterSetID The filter set to limit to. Use -1 for none (just indexes all the filtered systems in that case).
/// \param[in] index A number between 0 and GetSystemCount(filterSetID)-1;
SystemAddress GetSystemByIndex(int filterSetID, unsigned index);
/// Returns the total number of filter sets.
/// \return The total number of filter sets.
unsigned GetFilterSetCount(void) const;
/// Returns the ID of a filter set, by index
/// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive
int GetFilterSetIDByIndex(unsigned index);
/// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted.
/// \param[in] filterSetID The ID of the filter set to delete.
void DeleteFilterSet(int filterSetID);
// --------------------------------------------------------------------------------------------
// Packet handling functions
// --------------------------------------------------------------------------------------------
virtual void OnAttach(RakPeerInterface *peer);
virtual void OnDetach(RakPeerInterface *peer);
virtual void OnShutdown(RakPeerInterface *peer);
virtual void Update(RakPeerInterface *peer);
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress);
protected:
void Clear(void);
void DeallocateFilterSet(FilterSet *filterSet);
FilterSet* GetFilterSetByID(int filterSetID);
void OnInvalidMessage(RakPeerInterface *peer, FilterSet *filterSet, SystemAddress systemAddress, unsigned char messageID);
DataStructures::OrderedList<int, FilterSet*, FilterSetComp> filterList;
DataStructures::OrderedList<SystemAddress, FilteredSystem, FilteredSystemComp> systemList;
int autoAddNewConnectionsToFilter;
};
#endif

View File

@@ -0,0 +1,241 @@
/// \file
/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message.
///
/// This file is part of RakNet Copyright 2003 Kevin Jenkins.
///
/// Usage of RakNet is subject to the appropriate license agreement.
/// Creative Commons Licensees are subject to the
/// license found at
/// http://creativecommons.org/licenses/by-nc/2.5/
/// Single application licensees are subject to the license found at
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
/// Custom license users are subject to the terms therein.
/// GPL license users are subject to the GNU General Public
/// License as published by the Free
/// Software Foundation; either version 2 of the License, or (at your
/// option) any later version.
#ifndef __MESSAGE_IDENTIFIERS_H
#define __MESSAGE_IDENTIFIERS_H
#if defined(RAKNET_USE_CUSTOM_PACKET_IDS)
#include "CustomPacketIdentifiers.h"
#else
/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library
/// To define your own message id, define an enum following the code example that follows.
///
/// \code
/// enum {
/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM,
/// ID_MYPROJECT_MSG_2,
/// ...
/// };
/// \endcode
///
/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream
enum DefaultMessageIDTypes
{
//
// RESERVED TYPES - DO NOT CHANGE THESE
// All types from RakPeer
//
/// These types are never returned to the user.
/// Ping from a connected system. Update timestamps (internal use only)
ID_INTERNAL_PING,
/// Ping from an unconnected system. Reply but do not update timestamps. (internal use only)
ID_PING,
/// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only)
ID_PING_OPEN_CONNECTIONS,
/// Pong from a connected system. Update timestamps (internal use only)
ID_CONNECTED_PONG,
/// Asking for a new connection (internal use only)
ID_CONNECTION_REQUEST,
/// Connecting to a secured server/peer (internal use only)
ID_SECURED_CONNECTION_RESPONSE,
/// Connecting to a secured server/peer (internal use only)
ID_SECURED_CONNECTION_CONFIRMATION,
/// Packet that tells us the packet contains an integer ID to name mapping for the remote system (internal use only)
ID_RPC_MAPPING,
/// A reliable packet to detect lost connections (internal use only)
ID_DETECT_LOST_CONNECTIONS,
/// Offline message so we know when to reset and start a new connection (internal use only)
ID_OPEN_CONNECTION_REQUEST,
/// Offline message response so we know when to reset and start a new connection (internal use only)
ID_OPEN_CONNECTION_REPLY,
/// Remote procedure call (internal use only)
ID_RPC,
/// Remote procedure call reply, for RPCs that return data (internal use only)
ID_RPC_REPLY,
/// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT
ID_OUT_OF_BAND_INTERNAL,
//
// USER TYPES - DO NOT CHANGE THESE
//
/// RakPeer - In a client/server environment, our connection request to the server has been accepted.
ID_CONNECTION_REQUEST_ACCEPTED,
/// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect.
ID_CONNECTION_ATTEMPT_FAILED,
/// RakPeer - Sent a connect request to a system we are currently connected to.
ID_ALREADY_CONNECTED,
/// RakPeer - A remote system has successfully connected.
ID_NEW_INCOMING_CONNECTION,
/// RakPeer - The system we attempted to connect to is not accepting new connections.
ID_NO_FREE_INCOMING_CONNECTIONS,
/// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the server has shutdown.
ID_DISCONNECTION_NOTIFICATION,
/// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that system has been closed.
ID_CONNECTION_LOST,
/// RakPeer - We preset an RSA public key which does not match what the system we connected to is using.
ID_RSA_PUBLIC_KEY_MISMATCH,
/// RakPeer - We are banned from the system we attempted to connect to.
ID_CONNECTION_BANNED,
/// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password.
ID_INVALID_PASSWORD,
/// RakPeer - A packet has been tampered with in transit. The sender is contained in Packet::systemAddress.
ID_MODIFIED_PACKET,
/// RakPeer - The four bytes following this byte represent an unsigned int which is automatically modified by the difference in system times between the sender and the recipient. Requires that you call SetOccasionalPing.
ID_TIMESTAMP,
/// RakPeer - Pong from an unconnected system. First byte is ID_PONG, second sizeof(RakNetTime) bytes is the ping, following bytes is system specific enumeration data.
ID_PONG,
/// RakPeer - Inform a remote system of our IP/Port, plus some offline data
ID_ADVERTISE_SYSTEM,
/// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has disconnected gracefully. Packet::systemAddress is modified to reflect the systemAddress of this client.
ID_REMOTE_DISCONNECTION_NOTIFICATION,
/// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::systemAddress is modified to reflect the systemAddress of this client.
ID_REMOTE_CONNECTION_LOST,
/// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has connected. Packet::systemAddress is modified to reflect the systemAddress of the client that is not connected directly to us. The packet encoding is SystemAddress 1, ConnectionGraphGroupID 1, SystemAddress 2, ConnectionGraphGroupID 2
ID_REMOTE_NEW_INCOMING_CONNECTION,
// RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), partTotal (unsigned int), partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal and partLength in OnFileProgress in FileListTransferCBInterface.h
ID_DOWNLOAD_PROGRESS,
/// FileListTransfer plugin - Setup data
ID_FILE_LIST_TRANSFER_HEADER,
/// FileListTransfer plugin - A file
ID_FILE_LIST_TRANSFER_FILE,
/// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory
ID_DDT_DOWNLOAD_REQUEST,
/// RakNetTransport plugin - Transport provider message, used for remote console
ID_TRANSPORT_STRING,
/// ReplicaManager plugin - Create an object
ID_REPLICA_MANAGER_CONSTRUCTION,
/// ReplicaManager plugin - Destroy an object
ID_REPLICA_MANAGER_DESTRUCTION,
/// ReplicaManager plugin - Changed scope of an object
ID_REPLICA_MANAGER_SCOPE_CHANGE,
/// ReplicaManager plugin - Serialized data of an object
ID_REPLICA_MANAGER_SERIALIZE,
/// ReplicaManager plugin - New connection, about to send all world objects
ID_REPLICA_MANAGER_DOWNLOAD_STARTED,
/// ReplicaManager plugin - Finished downloading all serialized objects
ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE,
/// ConnectionGraph plugin - Request the connection graph from another system
ID_CONNECTION_GRAPH_REQUEST,
/// ConnectionGraph plugin - Reply to a connection graph download request
ID_CONNECTION_GRAPH_REPLY,
/// ConnectionGraph plugin - Update edges / nodes for a system with a connection graph
ID_CONNECTION_GRAPH_UPDATE,
/// ConnectionGraph plugin - Add a new connection to a connection graph
ID_CONNECTION_GRAPH_NEW_CONNECTION,
/// ConnectionGraph plugin - Remove a connection from a connection graph - connection was abruptly lost
ID_CONNECTION_GRAPH_CONNECTION_LOST,
/// ConnectionGraph plugin - Remove a connection from a connection graph - connection was gracefully lost
ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION,
/// Router plugin - route a message through another system
ID_ROUTE_AND_MULTICAST,
/// RakVoice plugin - Open a communication channel
ID_RAKVOICE_OPEN_CHANNEL_REQUEST,
/// RakVoice plugin - Communication channel accepted
ID_RAKVOICE_OPEN_CHANNEL_REPLY,
/// RakVoice plugin - Close a communication channel
ID_RAKVOICE_CLOSE_CHANNEL,
/// RakVoice plugin - Voice data
ID_RAKVOICE_DATA,
/// Autopatcher plugin - Get a list of files that have changed since a certain date
ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE,
/// Autopatcher plugin - A list of files to create
ID_AUTOPATCHER_CREATION_LIST,
/// Autopatcher plugin - A list of files to delete
ID_AUTOPATCHER_DELETION_LIST,
/// Autopatcher plugin - A list of files to get patches for
ID_AUTOPATCHER_GET_PATCH,
/// Autopatcher plugin - A list of patches for a list of files
ID_AUTOPATCHER_PATCH_LIST,
/// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher.
ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR,
/// Autopatcher plugin - Finished getting all files from the autopatcher
ID_AUTOPATCHER_FINISHED_INTERNAL,
ID_AUTOPATCHER_FINISHED,
/// Autopatcher plugin - Returned to the user: You must restart the application to finish patching.
ID_AUTOPATCHER_RESTART_APPLICATION,
/// NATPunchthrough plugin - Intermediary got a request to help punch through a nat
ID_NAT_PUNCHTHROUGH_REQUEST,
/// NATPunchthrough plugin - Intermediary cannot complete the request because the target system is not connected
ID_NAT_TARGET_NOT_CONNECTED,
/// NATPunchthrough plugin - While attempting to connect, we lost the connection to the target system
ID_NAT_TARGET_CONNECTION_LOST,
/// NATPunchthrough plugin - Internal message to connect at a certain time
ID_NAT_CONNECT_AT_TIME,
/// NATPunchthrough plugin - Internal message to send a message (to punch through the nat) at a certain time
ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME,
/// NATPunchthrough plugin - The facilitator is already attempting this connection
ID_NAT_IN_PROGRESS,
/// LightweightDatabase plugin - Query
ID_DATABASE_QUERY_REQUEST,
/// LightweightDatabase plugin - Update
ID_DATABASE_UPDATE_ROW,
/// LightweightDatabase plugin - Remove
ID_DATABASE_REMOVE_ROW,
/// LightweightDatabase plugin - A serialized table. Bytes 1+ contain the table. Pass to TableSerializer::DeserializeTable
ID_DATABASE_QUERY_REPLY,
/// LightweightDatabase plugin - Specified table not found
ID_DATABASE_UNKNOWN_TABLE,
/// LightweightDatabase plugin - Incorrect password
ID_DATABASE_INCORRECT_PASSWORD,
/// ReadyEvent plugin - Set the ready state for a particular system
ID_READY_EVENT_SET,
/// ReadyEvent plugin - Unset the ready state for a particular system
ID_READY_EVENT_UNSET,
/// All systems are in state ID_READY_EVENT_SET
ID_READY_EVENT_ALL_SET,
/// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting
ID_READY_EVENT_QUERY,
/// Lobby packets. Second byte indicates type.
ID_LOBBY_GENERAL,
/// Auto RPC procedure call
ID_AUTO_RPC_CALL,
/// Auto RPC functionName to index mapping
ID_AUTO_RPC_REMOTE_INDEX,
/// Auto RPC functionName to index mapping, lookup failed. Will try to auto recover
ID_AUTO_RPC_UNKNOWN_REMOTE_INDEX,
/// Auto RPC error code
/// See AutoRPC.h for codes, stored in packet->data[1]
ID_RPC_REMOTE_ERROR,
// For the user to use. Start your first enumeration at this value.
ID_USER_PACKET_ENUM,
//-------------------------------------------------------------------------------------------------------------
};
#endif // RAKNET_USE_CUSTOM_PACKET_IDS
#endif

Some files were not shown because too many files have changed in this diff Show More