Revert RakNet source files back to the original v2.518 state

* Add RakNet source files to the VS project
This commit is contained in:
RD42 2024-08-16 23:33:48 +08:00
parent 3bad4d20c2
commit bcdbedc0be
166 changed files with 46610 additions and 1598 deletions

View File

@ -212,45 +212,306 @@
<Filter
Name="raknet"
Filter="">
<File
RelativePath="..\raknet\_findfirst.cpp">
</File>
<File
RelativePath="..\raknet\_findfirst.h">
</File>
<File
RelativePath="..\raknet\AsynchronousFileIO.cpp">
</File>
<File
RelativePath="..\raknet\AsynchronousFileIO.h">
</File>
<File
RelativePath="..\raknet\AutopatcherPatchContext.h">
</File>
<File
RelativePath="..\raknet\AutopatcherRepositoryInterface.h">
</File>
<File
RelativePath="..\raknet\BigTypes.h">
</File>
<File
RelativePath="..\raknet\BitStream.cpp">
</File>
<File
RelativePath="..\raknet\BitStream.h">
</File>
<File
RelativePath="..\raknet\BitStream_NoTemplate.cpp">
</File>
<File
RelativePath="..\raknet\BitStream_NoTemplate.h">
</File>
<File
RelativePath="..\raknet\CheckSum.cpp">
</File>
<File
RelativePath="..\raknet\CheckSum.h">
</File>
<File
RelativePath="..\raknet\ClientContextStruct.h">
</File>
<File
RelativePath="..\raknet\CommandParserInterface.cpp">
</File>
<File
RelativePath="..\raknet\CommandParserInterface.h">
</File>
<File
RelativePath="..\raknet\ConnectionGraph.cpp">
</File>
<File
RelativePath="..\raknet\ConnectionGraph.h">
</File>
<File
RelativePath="..\raknet\ConsoleServer.cpp">
</File>
<File
RelativePath="..\raknet\ConsoleServer.h">
</File>
<File
RelativePath="..\raknet\DataBlockEncryptor.cpp">
</File>
<File
RelativePath="..\raknet\DataBlockEncryptor.h">
</File>
<File
RelativePath="..\raknet\DataCompressor.cpp">
</File>
<File
RelativePath="..\raknet\DataCompressor.h">
</File>
<File
RelativePath="..\raknet\DirectoryDeltaTransfer.cpp">
</File>
<File
RelativePath="..\raknet\DirectoryDeltaTransfer.h">
</File>
<File
RelativePath="..\raknet\DS_BinarySearchTree.h">
</File>
<File
RelativePath="..\raknet\DS_BPlusTree.h">
</File>
<File
RelativePath="..\raknet\DS_ByteQueue.cpp">
</File>
<File
RelativePath="..\raknet\DS_ByteQueue.h">
</File>
<File
RelativePath="..\raknet\DS_Heap.h">
</File>
<File
RelativePath="..\raknet\DS_HuffmanEncodingTree.cpp">
</File>
<File
RelativePath="..\raknet\DS_HuffmanEncodingTree.h">
</File>
<File
RelativePath="..\raknet\DS_HuffmanEncodingTreeFactory.h">
</File>
<File
RelativePath="..\raknet\DS_HuffmanEncodingTreeNode.h">
</File>
<File
RelativePath="..\raknet\DS_LinkedList.h">
</File>
<File
RelativePath="..\raknet\DS_List.h">
</File>
<File
RelativePath="..\raknet\DS_Map.h">
</File>
<File
RelativePath="..\raknet\DS_MemoryPool.h">
</File>
<File
RelativePath="..\raknet\DS_OrderedChannelHeap.h">
</File>
<File
RelativePath="..\raknet\DS_OrderedList.h">
</File>
<File
RelativePath="..\raknet\DS_Queue.h">
</File>
<File
RelativePath="..\raknet\DS_QueueLinkedList.h">
</File>
<File
RelativePath="..\raknet\DS_RangeList.h">
</File>
<File
RelativePath="..\raknet\DS_Table.cpp">
</File>
<File
RelativePath="..\raknet\DS_Table.h">
</File>
<File
RelativePath="..\raknet\DS_Tree.h">
</File>
<File
RelativePath="..\raknet\DS_WeightedGraph.h">
</File>
<File
RelativePath="..\raknet\EmailSender.cpp">
</File>
<File
RelativePath="..\raknet\EmailSender.h">
</File>
<File
RelativePath="..\raknet\EncodeClassName.cpp">
</File>
<File
RelativePath="..\raknet\Export.h">
</File>
<File
RelativePath="..\raknet\ExtendedOverlappedPool.cpp">
</File>
<File
RelativePath="..\raknet\ExtendedOverlappedPool.h">
</File>
<File
RelativePath="..\raknet\FileList.cpp">
</File>
<File
RelativePath="..\raknet\FileList.h">
</File>
<File
RelativePath="..\raknet\FileListTransfer.cpp">
</File>
<File
RelativePath="..\raknet\FileListTransfer.h">
</File>
<File
RelativePath="..\raknet\FileListTransferCBInterface.h">
</File>
<File
RelativePath="..\raknet\FileOperations.cpp">
</File>
<File
RelativePath="..\raknet\FileOperations.h">
</File>
<File
RelativePath="..\raknet\FullyConnectedMesh.cpp">
</File>
<File
RelativePath="..\raknet\FullyConnectedMesh.h">
</File>
<File
RelativePath="..\raknet\GetTime.cpp">
</File>
<File
RelativePath="..\raknet\GetTime.h">
</File>
<File
RelativePath="..\raknet\InternalPacket.h">
</File>
<File
RelativePath="..\raknet\InternalPacketPool.cpp">
</File>
<File
RelativePath="..\raknet\InternalPacketPool.h">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseClient.cpp">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseClient.h">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseCommon.cpp">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseCommon.h">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseServer.cpp">
</File>
<File
RelativePath="..\raknet\LightweightDatabaseServer.h">
</File>
<File
RelativePath="..\raknet\LinuxStrings.cpp">
</File>
<File
RelativePath="..\raknet\LinuxStrings.h">
</File>
<File
RelativePath="..\raknet\LogCommandParser.cpp">
</File>
<File
RelativePath="..\raknet\LogCommandParser.h">
</File>
<File
RelativePath="..\raknet\Makefile">
</File>
<File
RelativePath="..\raknet\Makefile.am">
</File>
<File
RelativePath="..\raknet\MTUSize.h">
</File>
<File
RelativePath="..\raknet\Multiplayer.h">
</File>
<File
RelativePath="..\raknet\NatPunchthrough.cpp">
</File>
<File
RelativePath="..\raknet\NatPunchthrough.h">
</File>
<File
RelativePath="..\raknet\NetworkIDGenerator.cpp">
</File>
<File
RelativePath="..\raknet\NetworkIDGenerator.h">
</File>
<File
RelativePath="..\raknet\NetworkTypes.cpp">
</File>
<File
RelativePath="..\raknet\NetworkTypes.h">
</File>
<File
RelativePath="..\raknet\PacketConsoleLogger.cpp">
</File>
<File
RelativePath="..\raknet\PacketConsoleLogger.h">
</File>
<File
RelativePath="..\raknet\PacketEnumerations.h">
</File>
<File
RelativePath="..\raknet\PacketFileLogger.cpp">
</File>
<File
RelativePath="..\raknet\PacketFileLogger.h">
</File>
<File
RelativePath="..\raknet\PacketLogger.cpp">
</File>
<File
RelativePath="..\raknet\PacketLogger.h">
</File>
<File
RelativePath="..\raknet\PacketPool.h">
</File>
<File
RelativePath="..\raknet\PacketPriority.h">
</File>
<File
RelativePath="..\raknet\PluginInterface.cpp">
</File>
<File
RelativePath="..\raknet\PluginInterface.h">
</File>
<File
RelativePath="..\raknet\RakAssert.h">
</File>
<File
RelativePath="..\raknet\RakClient.cpp">
</File>
@ -260,12 +521,27 @@
<File
RelativePath="..\raknet\RakClientInterface.h">
</File>
<File
RelativePath="..\raknet\RakNetCommandParser.cpp">
</File>
<File
RelativePath="..\raknet\RakNetCommandParser.h">
</File>
<File
RelativePath="..\raknet\RakNetDefines.h">
</File>
<File
RelativePath="..\raknet\RakNetStatistics.cpp">
</File>
<File
RelativePath="..\raknet\RakNetStatistics.h">
</File>
<File
RelativePath="..\raknet\RakNetTransport.cpp">
</File>
<File
RelativePath="..\raknet\RakNetTransport.h">
</File>
<File
RelativePath="..\raknet\RakNetworkFactory.cpp">
</File>
@ -308,6 +584,36 @@
<File
RelativePath="..\raknet\ReliabilityLayer.h">
</File>
<File
RelativePath="..\raknet\Replica.h">
</File>
<File
RelativePath="..\raknet\ReplicaEnums.h">
</File>
<File
RelativePath="..\raknet\ReplicaManager.cpp">
</File>
<File
RelativePath="..\raknet\ReplicaManager.h">
</File>
<File
RelativePath="..\raknet\rijndael-boxes.h">
</File>
<File
RelativePath="..\raknet\rijndael.cpp">
</File>
<File
RelativePath="..\raknet\rijndael.h">
</File>
<File
RelativePath="..\raknet\Router.cpp">
</File>
<File
RelativePath="..\raknet\Router.h">
</File>
<File
RelativePath="..\raknet\RouterInterface.h">
</File>
<File
RelativePath="..\raknet\RPCMap.cpp">
</File>
@ -317,6 +623,9 @@
<File
RelativePath="..\raknet\RPCNode.h">
</File>
<File
RelativePath="..\raknet\RSACrypt.h">
</File>
<File
RelativePath="..\raknet\SAMPRPC.h">
</File>
@ -332,6 +641,9 @@
<File
RelativePath="..\raknet\SimpleMutex.h">
</File>
<File
RelativePath="..\raknet\SimpleTCPServer.h">
</File>
<File
RelativePath="..\raknet\SingleProducerConsumer.h">
</File>
@ -347,6 +659,42 @@
<File
RelativePath="..\raknet\SocketLayer.h">
</File>
<File
RelativePath="..\raknet\StringCompressor.cpp">
</File>
<File
RelativePath="..\raknet\StringCompressor.h">
</File>
<File
RelativePath="..\raknet\StringTable.cpp">
</File>
<File
RelativePath="..\raknet\StringTable.h">
</File>
<File
RelativePath="..\raknet\SystemAddressList.cpp">
</File>
<File
RelativePath="..\raknet\SystemAddressList.h">
</File>
<File
RelativePath="..\raknet\SystemDatabaseClient.cpp">
</File>
<File
RelativePath="..\raknet\SystemDatabaseClient.h">
</File>
<File
RelativePath="..\raknet\SystemDatabaseServer.cpp">
</File>
<File
RelativePath="..\raknet\SystemDatabaseServer.h">
</File>
<File
RelativePath="..\raknet\TableSerializer.cpp">
</File>
<File
RelativePath="..\raknet\TableSerializer.h">
</File>
<File
RelativePath="..\raknet\TCPInterface.cpp">
</File>
@ -359,6 +707,18 @@
<File
RelativePath="..\raknet\TEABlockEncryptor.h">
</File>
<File
RelativePath="..\raknet\TelnetTransport.cpp">
</File>
<File
RelativePath="..\raknet\TelnetTransport.h">
</File>
<File
RelativePath="..\raknet\ThreadPool.h">
</File>
<File
RelativePath="..\raknet\TransportInterface.h">
</File>
<File
RelativePath="..\raknet\Types.h">
</File>

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.rakkarsoft.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 "NetworkTypes.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(_COMPATIBILITY_1) && 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(_COMPATIBILITY_1) && 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(_COMPATIBILITY_1) && 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.rakkarsoft.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 _COMPATIBILITY_1
#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
*/

View File

@ -0,0 +1,15 @@
#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,
};
#endif

View File

@ -0,0 +1,52 @@
/// \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.rakkarsoft.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
/// \return True on success, false on failure.
virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList)=0;
/// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false.
virtual char *GetLastError(void) const=0;
};
#endif

1820
raknet/BigTypes.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,59 @@
// TODO: Implement BitStream.cpp
/// \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.rakkarsoft.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 <stdlib.h>
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>
#ifdef _COMPATIBILITY_1
#include "Compatibility1Includes.h"
#elif defined(_WIN32)
#include <winsock2.h> // htonl
#else
#include <arpa/inet.h>
#endif
// Was included for memset which now comes from string.h instead
/*
#if defined ( __APPLE__ ) || defined ( __APPLE_CC__ )
#include <malloc/malloc.h>
#elif !defined(_COMPATIBILITY_2)
#include <malloc.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;
@ -75,41 +123,6 @@ BitStream::BitStream( unsigned char* _data, unsigned int lengthInBytes, bool _co
data = ( unsigned char* ) _data;
}
// SAMPSRV (adding this just as a tag for next RakNet upgrade)
BitStream::BitStream( char* _dataC, unsigned int lengthInBytes, bool _copyData )
{
unsigned char* _data = reinterpret_cast<unsigned char*>(_dataC);
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* ) malloc( lengthInBytes );
}
#ifdef _DEBUG
assert( data );
#endif
memcpy( data, _data, 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 unsigned int lengthInBits )
{
@ -148,5 +161,622 @@ void BitStream::Reset( void )
// memset(data, 0, BITS_TO_BYTES(numberOfBitsAllocated));
}
// Write an array or casted stream
void BitStream::Write( const char* input, const int numberOfBytes )
{
if (numberOfBytes==0)
return;
// Optimization:
if ((numberOfBitsUsed & 7) == 0)
{
AddBitsAndReallocate( BYTES_TO_BITS(numberOfBytes) );
memcpy(data+BITS_TO_BYTES(numberOfBitsUsed), input, 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, int numberOfBits )
{
AddBitsAndReallocate( numberOfBits );
int 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++ % 8 ) ) )
{
// 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++ % 8 ) ) )
data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
// else 0, do nothing
}
numberOfBitsUsed++;
}
}
// Read an array or casted stream
bool BitStream::Read( char* output, const int numberOfBytes )
{
// Optimization:
if ((readOffset & 7) == 0)
{
if ( readOffset + ( numberOfBytes << 3 ) > numberOfBitsUsed )
return false;
// Write the data
memcpy( output, data + ( readOffset >> 3 ), 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 );
int numberOfBitsMod8 = numberOfBitsUsed & 7;
if ( numberOfBitsMod8 == 0 )
data[ numberOfBitsUsed >> 3 ] = 0x80;
else
data[ numberOfBitsUsed >> 3 ] |= 0x80 >> ( numberOfBitsMod8 ); // Set the bit to 1
numberOfBitsUsed++;
}
#ifdef _MSC_VER
#pragma warning( disable : 4800 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
// Returns true if the next data read is a 1, false if it is a 0
bool BitStream::ReadBit( void )
{
return ( bool ) ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset++ & 7 ) ) );
}
// 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 int numberOfBytesToWrite )
{
#ifdef _DEBUG
assert( numberOfBytesToWrite > 0 );
#endif
AlignWriteToByteBoundary();
Write((const char*) input, numberOfBytesToWrite);
}
// 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 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 ), numberOfBytesToRead );
readOffset += numberOfBytesToRead << 3;
return true;
}
// 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, int numberOfBitsToWrite, const bool rightAlignedBits )
{
if (numberOfBitsToWrite<=0)
return;
AddBitsAndReallocate( numberOfBitsToWrite );
int offset = 0;
unsigned char dataByte;
int 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;
numberOfBitsToWrite -= 8;
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 int size, const bool unsignedData )
{
int 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, int numberOfBitsToRead, const bool alignBitsToRight )
{
#ifdef _DEBUG
assert( numberOfBitsToRead > 0 );
#endif
if (numberOfBitsToRead<=0)
return false;
if ( readOffset + numberOfBitsToRead > numberOfBitsUsed )
return false;
int readOffsetMod8;
int offset = 0;
memset( output, 0, BITS_TO_BYTES( numberOfBitsToRead ) );
readOffsetMod8 = readOffset & 7;
// do
// Faster to put the while at the top surprisingly enough
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)
numberOfBitsToRead -= 8;
if ( numberOfBitsToRead < 0 ) // Reading a partial byte for the last byte, shift right so the data is aligned on the right
{
if ( alignBitsToRight )
* ( output + offset ) >>= -numberOfBitsToRead;
readOffset += 8 + numberOfBitsToRead;
}
else
readOffset += 8;
offset++;
}
//} while(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 int size, const bool unsignedData )
{
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 int numberOfBitsToWrite )
{
if (numberOfBitsToWrite <= 0)
return;
int 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
assert( copyData == true );
#endif
// Less memory efficient but saves on news and deletes
newNumberOfBitsAllocated = ( numberOfBitsToWrite + numberOfBitsUsed ) * 2;
// int newByteOffset = BITS_TO_BYTES( numberOfBitsAllocated );
// Use realloc and free so we are more efficient than delete and new for resizing
int amountToAllocate = BITS_TO_BYTES( newNumberOfBitsAllocated );
if (data==(unsigned char*)stackData)
{
if (amountToAllocate > BITSTREAM_STACK_ALLOCATION_SIZE)
{
data = ( unsigned char* ) malloc( amountToAllocate );
// need to copy the stack data over to our new memory area too
memcpy ((void *)data, (void *)stackData, BITS_TO_BYTES( numberOfBitsAllocated ));
}
}
else
{
data = ( unsigned char* ) realloc( data, 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;
}
// 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 ( int counter = 0; counter < BITS_TO_BYTES( numberOfBitsUsed ); counter++ )
{
int stop;
if ( counter == ( numberOfBitsUsed - 1 ) >> 3 )
stop = 8 - ( ( ( numberOfBitsUsed - 1 ) & 7 ) + 1 );
else
stop = 0;
for ( int counter2 = 7; counter2 >= stop; counter2-- )
{
if ( ( data[ counter ] >> counter2 ) & 1 )
putchar( '1' );
else
putchar( '0' );
}
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.
int BitStream::CopyData( unsigned char** _data ) const
{
#ifdef _DEBUG
assert( numberOfBitsUsed > 0 );
#endif
*_data = new unsigned char [ BITS_TO_BYTES( numberOfBitsUsed ) ];
memcpy( *_data, data, sizeof(unsigned char) * ( BITS_TO_BYTES( numberOfBitsUsed ) ) );
return numberOfBitsUsed;
}
// Ignore data we don't intend to read
void BitStream::IgnoreBits( const int numberOfBits )
{
readOffset += numberOfBits;
}
// Move the write pointer to a position on the array. Dangerous if you don't know what you are doing!
void BitStream::SetWriteOffset( const int offset )
{
numberOfBitsUsed = offset;
}
/*
int BitStream::GetWriteOffset( void ) const
{
return numberOfBitsUsed;
}
// Returns the length in bits of the stream
int BitStream::GetNumberOfBitsUsed( void ) const
{
return GetWriteOffset();
}
// Returns the length in bytes of the stream
int BitStream::GetNumberOfBytesUsed( void ) const
{
return BITS_TO_BYTES( numberOfBitsUsed );
}
// Returns the number of bits into the stream that we have read
int BitStream::GetReadOffset( void ) const
{
return readOffset;
}
// Sets the read bit index
void BitStream::SetReadOffset( int newReadOffset )
{
readOffset=newReadOffset;
}
// Returns the number of bits left in the stream that haven't been read
int 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* ) malloc( BITS_TO_BYTES( numberOfBitsAllocated ) );
#ifdef _DEBUG
assert( data );
#endif
memcpy( newdata, data, BITS_TO_BYTES( numberOfBitsAllocated ) );
data = newdata;
}
else
data = 0;
}
}
void BitStream::ReverseBytes(unsigned char *input, unsigned char *output, int length)
{
for (int i=0; i < length; i++)
output[i]=input[length-i-1];
}
bool BitStream::DoEndianSwap(void) const
{
#ifndef __BITSTREAM_NATIVE_END
static bool swap=htonl(12345) == 12345;
return swap;
#else
return false;
#endif
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif // #if _MSC_VER < 1299

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,713 @@
/// \depreciated
#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization
#ifndef __BITSTREAM_H
#define __BITSTREAM_H
#ifdef _WIN32
#if defined (_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 64
typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define HAS_INT64
#endif
#else
#include <stdint.h>
#define HAS_INT64
#endif
// Arbitrary size, just picking something likely to be larger than most packets
#define BITSTREAM_STACK_ALLOCATION_SIZE 256
/** \note If you want the default network byte stream to be
in Network Byte Order (Big Endian) then #define __BITSTREAM_BIG_END
otherwise the default is 'Little Endian'. If your CPU has the same
Byte Order as your network stream, you can cut out some overheads
using #define __BITSTREAM_NATIVE_END --- if this is defined,
the __BITSTREAM_BIG_END flag becomes ineffective.
*/
#include "NetworkTypes.h"
namespace RakNet
{
/**
* This macro transforms a bit in byte
* @param x Transform a bit to a byte
*/
#define BITS_TO_BYTES(x) (((x)+7)>>3)
#define BYTES_TO_BITS(x) (x<<3)
/**
* @brief Packets encoding and decoding facilities
*
* Helper class to encode and decode packets.
*
*/
class BitStream
{
public:
/**
* Default Constructor
*/
BitStream();
/**
* Preallocate some memory for the construction of the packet
* @param initialBytesToAllocate the amount of byte to pre-allocate.
*/
BitStream( int initialBytesToAllocate );
/**
* Initialize the BitStream object using data from the network.
* Set _copyData to true if you want to make an internal copy of
* the data you are passing. You can then Write and do all other
* operations Set it to false if you want to just use a pointer to
* the data you are passing, in order to save memory and speed.
* You should only then do read operations.
* @param _data An array of bytes.
* @param lengthInBytes Size of the @em _data.
* @param _copyData Does a copy of the input data.
*/
BitStream( char* _data, unsigned int lengthInBytes, bool _copyData );
/**
* Destructor
*/
~BitStream();
/**
* Reset the bitstream for reuse
*/
void Reset( void );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const bool input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const unsigned char input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const char input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const unsigned short input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const short input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const unsigned int input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const int input );
#if defined ( __APPLE__ ) || defined (__APPLE_CC__ )||defined ( _WIN32 )
// These are only provided for MS Windows and
// Mac OSX (G4 processor) convenience and are
// equivalent to the (int) versions.
// The use of 'long' for any network data is
// a fault since it will not be portable to 64-bit CPUs.
void Write( const unsigned long input );
void Write( const long input );
#endif
#ifdef HAS_INT64
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const uint64_t input );
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const int64_t input );
#endif
/**
* Write the native types to the end of the buffer
* without any compression mecanism.
* @param input The data
*/
void Write( const float input );
/**
* Write the native types to the end of the buffer
* without any compression mechanism.
* @param input The data
*/
void Write( const double input );
/**
* Write an array or casted stream. It is supposed to
* be raw data. It is also not possible to deal with endian problem
* @param input a byte buffer
* @param numberOfBytes the size of the byte buffer
*/
void Write( const char* input, const int numberOfBytes );
void Write( const NetworkID networkId );
/**
* Copy from another bitstream
* @bitStream the bitstream to copy from
*/
void Write( const BitStream *bitStream );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const unsigned char input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const char input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const unsigned short input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const short input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const unsigned int input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const int input );
#if defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) || defined ( _WIN32 )
// These are only provided for MS Windows and
// Mac OSX (G4 processor) convenience and are
// equivalent to the (int) versions.
// The use of 'long' for any network data is
// a fault since it will not be portable to 64-bit CPUs.
void WriteCompressed( const unsigned long input );
void WriteCompressed( const long input );
#endif
#ifdef HAS_INT64
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const uint64_t input );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const int64_t input );
#endif
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const float input );
/**
* Write a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12 bytes. Will further compress y or z axis aligned vectors.
* Accurate to 1/32767.5.
* @param x x
* @param y y
* @param z z
*/
void 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 x x
* @param y y
* @param z z
*/
void WriteVector( float x, float y, float z );
/**
* Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
* @param w w
* @param x x
* @param y y
* @param z z
*/
void 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
*/
void WriteOrthMatrix(
float m00, float m01, float m02,
float m10, float m11, float m12,
float m20, float m21, float m22 );
/**
* Write the native types with simple compression.
* Best used with negatives and positives close to 0
* @param input The data.
*/
void WriteCompressed( const double input );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( bool &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( unsigned char &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( char &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( unsigned short &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( short &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( unsigned int &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( int &output );
#if defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) || defined ( _WIN32 )
// These are only provided for MS Windows and
// Mac OSX (G4 processor) convenience and are
// equivalent to the (int) versions.
// The use of 'long' for any network data is
// a fault since it will not be portable to 64-bit CPUs.
bool Read( unsigned long &output );
bool Read( long &output );
#endif
#ifdef HAS_INT64
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( uint64_t &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( int64_t &output );
#endif
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( float &output );
/**
* Read the native types from the front of the buffer
* @param output The readed value.
* @return true on success false otherwise. The result of a reading
* can only be wrong in the case we reach the end of the BitStream
* with some missing bits.
*/
bool Read( double &output );
/**
* Read an array or casted stream of byte. The array
* is raw data. There is no automatic conversion on
* big endian arch
* @param output The result byte array. It should be larger than @em numberOfBytes.
* @param 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 );
bool Read( NetworkID &output);
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( unsigned char & output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( char &output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( unsigned short &output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( short &output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( unsigned int &output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( int &output );
#if defined ( __APPLE__ ) || defined ( __APPLE_CC__ )|| defined ( _WIN32 )
// These are only provided for MS Windows and
// Mac OSX (G4 processor) convenience and are
// equivalent to the (int) versions.
// The use of 'long' for any network data is
// a fault since it will not be portable to 64-bit CPUs.
bool ReadCompressed( unsigned long &output );
bool ReadCompressed( long &output );
#endif
#ifdef HAS_INT64
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( uint64_t &output );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( int64_t &output );
#endif
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( float &output );
/**
* Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12 bytes. Will further compress y or z axis aligned vectors.
* Accurate to 1/32767.5.
* @param x x
* @param y y
* @param z z
*/
bool ReadNormVector( float &x, float &y, float &z );
/**
* Read 3 floats, using 10 bytes, where those floats comprise a vector
* Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important.
* @param x x
* @param y y
* @param z z
*/
bool ReadVector( float &x, float &y, float &z );
/**
* Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy.
* @param w w
* @param x x
* @param y y
* @param z z
*/
bool ReadNormQuat( float &w, float &x, float &y, float &z);
/**
* Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolatig the 4th.
* for 6 bytes instead of 36
*/
bool ReadOrthMatrix(
float &m00, float &m01, float &m02,
float &m10, float &m11, float &m12,
float &m20, float &m21, float &m22 );
/**
* Read the types you wrote with WriteCompressed
* @param output The read value
* @return true on success, false on not enough data to read
*/
bool ReadCompressed( double &output );
/**
* 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 );
/**
* print to the standard output the state of the stream bit by bit
*/
void PrintBits( void ) const;
/**
* Ignore data we don't intend to read
* @param numberOfBits The number of bits to ignore
*/
void IgnoreBits( const int numberOfBits );
/**
* Move the write pointer to a position on the array.
* @param offset the offset from the start of the array.
* @attention
* Dangerous if you don't know what you are doing!
*
*/
void SetWriteOffset( const int offset );
/**
* Returns the length in bits of the stream
*/
int GetNumberOfBitsUsed( void ) const;
/**
* Returns the length in bytes of the stream
*/
int GetNumberOfBytesUsed( void ) const;
/**
* Returns the number of bits into the stream that we have read
*/
int GetReadOffset( void ) const;
/**
* Returns the number of bits left in the stream that haven't been read
*/
int GetNumberOfUnreadBits( void ) const;
/**
* Makes a copy of the internal data for you Data will point to
* the stream. Returns the length in bits of the stream. Partial
* bytes are left aligned
* @param _data the resulting byte copy of the internal state.
*/
int CopyData( unsigned char** _data ) const;
/**
* Set the stream to some initial data. For internal use
* Partial bytes are left aligned
* @param input The data
* @param numberOfBits the number of bits set in the data buffer
*/
void SetData( const unsigned char* input, const int numberOfBits );
/**
* Exposes the internal data.
* Partial bytes are left aligned.
* @return A pointer to the internal state
*/
unsigned char* GetData( void ) const;
/**
* 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 input The data
* @param numberOfBitsToWrite The number of bits to write
* @param 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 input The data
* @param numberOfBytesToWrite The size of data.
*/
void WriteAlignedBytes( const unsigned char* input,
const int numberOfBytesToWrite );
/**
* 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 output The byte array larger than @em numberOfBytesToRead
* @param numberOfBytesToRead The number of byte to read from the internal state
* @return true if there is enough byte.
*/
bool ReadAlignedBytes( unsigned char* output,
const int numberOfBytesToRead );
/**
* 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 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 output The resulting bits array
* @param numberOfBitsToRead The number of bits to read
* @param alignsBitsToRight 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 );
/**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* Write a 0
*/
void Write0( void );
/**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* Write a 1
*/
void Write1( void );
/**
* --- Low level functions ---
* These are for when you want to deal
* with bits and don't care about type checking
* 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 );
private:
/**
* 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 );
/**
* Reallocates (if necessary) in preparation of writing
* numberOfBitsToWrite
*/
void AddBitsAndReallocate( const int numberOfBitsToWrite );
/**
* Number of bits currently used
*/
int numberOfBitsUsed;
/**
* Number of bits currently allocated
*/
int numberOfBitsAllocated;
/**
* Current readOffset
*/
int readOffset;
/**
* array of byte storing the data. Points to stackData or if is bigger than that then is allocated
*/
unsigned char *data;
/**
* true if the internal buffer is copy of the data passed to the
* constructor
*/
bool copyData;
unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE];
};
}
#endif
#endif

97
raknet/CheckSum.cpp 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)

52
raknet/CheckSum.h Normal file
View File

@ -0,0 +1,52 @@
/// \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
/// Generates and validates checksums
class CheckSum
{
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.rakkarsoft.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 _COMPATIBILITY_1
#elif defined(_WIN32)
//#include <windows.h>
#endif
#include "NetworkTypes.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,174 @@
#include "CommandParserInterface.h"
#include "TransportInterface.h"
#include <string.h>
#include <assert.h>
#include <stdio.h>
#ifdef _COMPATIBILITY_1
#include "Compatibility1Includes.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
#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_stricmp)
#define _stricmp strcasecmp
#endif
#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, PlayerID playerId)
{
unsigned i;
if (commandList.Size())
{
for (i=0; i < commandList.Size(); i++)
{
transport->Send(playerId, "%s", commandList[i].command);
if (i < commandList.Size()-1)
transport->Send(playerId, ", ");
}
transport->Send(playerId, "\r\n");
}
else
transport->Send(playerId, "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);
}
bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc)
{
bool objectExists;
unsigned index;
index=commandList.GetIndexFromKey(command, &objectExists);
if (objectExists)
*rc=commandList[index];
return objectExists;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void CommandParserInterface::OnTransportChange(TransportInterface *transport)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void CommandParserInterface::OnNewIncomingConnection(PlayerID playerId, TransportInterface *transport)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void CommandParserInterface::OnConnectionLost(PlayerID playerId, TransportInterface *transport)
{
}
void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, PlayerID playerId)
{
if (res)
transport->Send(playerId, "%s returned true.\r\n", command);
else
transport->Send(playerId, "%s returned false.\r\n", command);
}
void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, PlayerID playerId)
{
transport->Send(playerId, "%s returned %i.\r\n", command, res);
}
void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, PlayerID playerId)
{
transport->Send(playerId, "Successfully called %s.\r\n", command);
}
void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, PlayerID playerId)
{
transport->Send(playerId, "%s returned %s.\r\n", command, res);
}
void CommandParserInterface::ReturnResult(PlayerID res, const char *command, TransportInterface *transport, PlayerID playerId)
{
#if !defined(_COMPATIBILITY_1)
in_addr in;
in.s_addr = playerId.binaryAddress;
inet_ntoa( in );
transport->Send(playerId, "%s returned %s %i:%i\r\n", command,inet_ntoa( in ),res.binaryAddress, res.port);
#else
transport->Send(playerId, "%s returned %i:%i\r\n", command,res.binaryAddress, res.port);
#endif
}
PlayerID CommandParserInterface::IntegersToPlayerID(int binaryAddress, int port)
{
PlayerID playerId;
playerId.binaryAddress=binaryAddress;
playerId.port=(unsigned short)port;
return playerId;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@ -0,0 +1,148 @@
/// \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.rakkarsoft.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 "NetworkTypes.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:
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 char *GetName(void) const=0;
/// A callback for when \a playerId has connected to us.
/// \param[in] playerId 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(PlayerID playerId, TransportInterface *transport);
/// A callback for when \a playerId has disconnected, either gracefully or forcefully
/// \param[in] playerId The player that has disconnected.
/// \param[in] transport The transport interface that sent us this information.
virtual void OnConnectionLost(PlayerID playerId, TransportInterface *transport);
/// A callback for when you are expected to send a brief description of your parser to \a playerId
/// \param[in] transport The transport interface we can use to write to
/// \param[in] playerId The player that requested help.
virtual void SendHelp(TransportInterface *transport, PlayerID playerId)=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] playerId 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, PlayerID playerId, 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] playerId The player to write to
virtual void SendCommandList(TransportInterface *transport, PlayerID playerId);
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] playerId The player this result will be sent to
virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, PlayerID playerId);
virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, PlayerID playerId);
virtual void ReturnResult(PlayerID res, const char *command, TransportInterface *transport, PlayerID playerId);
virtual void ReturnResult(int res, const char *command,TransportInterface *transport, PlayerID playerId);
/// 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] playerId The player this result will be sent to
virtual void ReturnResult(const char *command,TransportInterface *transport, PlayerID playerId);
/// Since there's no way to specify a playerID directly, the user needs to specify both the binary address and port.
/// Given those parameters, this returns the corresponding PlayerID
/// \param[in] binaryAddress The binaryAddress portion of PlayerID
/// \param[in] port The port portion of PlayerID
PlayerID IntegersToPlayerID(int binaryAddress, int port);
DataStructures::OrderedList<const char*, RegisteredCommand, RegisteredCommandComp> commandList;
};
#endif

623
raknet/ConnectionGraph.cpp Normal file
View File

@ -0,0 +1,623 @@
/// \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.rakkarsoft.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 "PacketEnumerations.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
extern Packet *AllocPacket(unsigned dataSize);
static const int connectionGraphChannel=31;
ConnectionGraph::PlayerIdAndGroupId::PlayerIdAndGroupId()
{
}
ConnectionGraph::PlayerIdAndGroupId::~PlayerIdAndGroupId()
{
}
ConnectionGraph::PlayerIdAndGroupId::PlayerIdAndGroupId(PlayerID playerId, ConnectionGraphGroupID groupID)
{
this->playerId=playerId;
this->groupId=groupId;
}
bool ConnectionGraph::PlayerIdAndGroupId::operator==( const ConnectionGraph::PlayerIdAndGroupId& right ) const
{
return playerId==right.playerId;
}
bool ConnectionGraph::PlayerIdAndGroupId::operator!=( const ConnectionGraph::PlayerIdAndGroupId& right ) const
{
return playerId!=right.playerId;
}
bool ConnectionGraph::PlayerIdAndGroupId::operator > ( const ConnectionGraph::PlayerIdAndGroupId& right ) const
{
return playerId>right.playerId;
}
bool ConnectionGraph::PlayerIdAndGroupId::operator < ( const ConnectionGraph::PlayerIdAndGroupId& right ) const
{
return playerId<right.playerId;
}
ConnectionGraph::ConnectionGraph()
{
pw=0;
myGroupId=0;
autoAddNewConnections=true;
// forceBroadcastTime=0;
DataStructures::WeightedGraph<ConnectionGraph::PlayerIdAndGroupId, unsigned short, false>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<PlayerID, PlayerID>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<ConnectionGraph::PlayerIdAndGroupId, ConnectionGraph::PlayerIdAndGroupId>::IMPLEMENT_DEFAULT_COMPARISON();
DataStructures::OrderedList<ConnectionGraphGroupID, ConnectionGraphGroupID>::IMPLEMENT_DEFAULT_COMPARISON();
subscribedGroups.Insert(0,0);
}
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=new char [strlen(password)+1];
strcpy(pw, password);
}
}
DataStructures::WeightedGraph<ConnectionGraph::PlayerIdAndGroupId, unsigned short, false> *ConnectionGraph::GetGraph(void)
{
return &graph;
}
void ConnectionGraph::SetAutoAddNewConnections(bool autoAdd)
{
autoAddNewConnections=autoAdd;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void ConnectionGraph::OnDisconnect(RakPeerInterface *peer)
{
graph.Clear();
participantList.Clear();
// forceBroadcastTime=0;
}
void ConnectionGraph::Update(RakPeerInterface *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<PlayerID,PlayerID> 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_STOP_PROCESSING_AND_DEALLOCATE;
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 toe 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->playerId, packetId);
}
}
return RR_CONTINUE_PROCESSING;
}
void ConnectionGraph::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId)
{
HandleDroppedConnection(peer, playerId, ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION);
}
void ConnectionGraph::HandleDroppedConnection(RakPeerInterface *peer, PlayerID playerId, unsigned char packetId)
{
assert(peer);
RemoveParticipant(playerId);
DataStructures::OrderedList<PlayerID,PlayerID> ignoreList;
RemoveAndRelayConnection(ignoreList, packetId, playerId, peer->GetExternalID(playerId), peer);
}
void ConnectionGraph::OnNewIncomingConnection(RakPeerInterface *peer, Packet *packet)
{
if (autoAddNewConnections==false)
return;
// 0 is the default groupId
AddNewConnection(peer, packet->playerId, 0);
}
void ConnectionGraph::OnConnectionRequestAccepted(RakPeerInterface *peer, Packet *packet)
{
if (autoAddNewConnections==false)
return;
RequestConnectionGraph(peer, packet->playerId);
// 0 is the default groupId
AddNewConnection(peer, packet->playerId, 0);
}
void ConnectionGraph::SetGroupId(ConnectionGraphGroupID groupId)
{
myGroupId=groupId;
}
void ConnectionGraph::AddNewConnection(RakPeerInterface *peer, PlayerID playerId, ConnectionGraphGroupID groupId)
{
if (autoAddNewConnections==false && subscribedGroups.HasData(groupId)==false)
return;
DataStructures::OrderedList<PlayerID,PlayerID> ignoreList;
PlayerIdAndGroupId first, second;
first.playerId=playerId;
first.groupId=groupId;
second.playerId=peer->GetExternalID(playerId);
second.groupId=myGroupId;
AddAndRelayConnection(ignoreList, first, second, (unsigned short)peer->GetAveragePing(playerId), peer);
}
void ConnectionGraph::SubscribeToGroup(ConnectionGraphGroupID groupId)
{
subscribedGroups.Insert(groupId, groupId);
}
void ConnectionGraph::UnsubscribeFromGroup(ConnectionGraphGroupID groupId)
{
subscribedGroups.Remove(groupId);
}
void ConnectionGraph::RequestConnectionGraph(RakPeerInterface *peer, PlayerID playerId)
{
RakNet::BitStream outBitstream;
outBitstream.Write((unsigned char) ID_CONNECTION_GRAPH_REQUEST);
stringCompressor->EncodeString(pw,256,&outBitstream);
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, playerId, false);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("ID_CONNECTION_GRAPH_REQUEST from %i to %i\n", peer->GetInternalID().port, playerId.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((unsigned char) ID_CONNECTION_GRAPH_REPLY);
stringCompressor->EncodeString(pw,256,&outBitstream);
SerializeWeightedGraph(&outBitstream, graph);
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->playerId, false);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("from %i to %i\n", peer->GetInternalID().port, packet->playerId.port);
#endif
// Add packet->playerId to the participant list if it is not already there
AddParticipant(packet->playerId);
}
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((unsigned char) 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->playerId));
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("from %i to %i\n", peer->GetInternalID().port, packet->playerId.port);
#endif
peer->Send(&outBitstream, LOW_PRIORITY, RELIABLE_ORDERED, connectionGraphChannel, packet->playerId, false);
// Add packet->playerId to the participant list if it is not already there
AddParticipant(packet->playerId);
if (DeserializeWeightedGraph(&inBitstream, peer)==false)
return;
// Forward the updated graph to all current participants
DataStructures::OrderedList<PlayerID,PlayerID> ignoreList;
ignoreList.Insert(packet->playerId,packet->playerId);
BroadcastGraphUpdate(ignoreList, peer);
}
void ConnectionGraph::OnConnectionGraphUpdate(RakPeerInterface *peer, Packet *packet)
{
// Only accept from participants
if (participantList.HasData(packet->playerId)==false)
return;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
if (DeserializeWeightedGraph(&inBitstream, peer)==false)
return;
DataStructures::OrderedList<PlayerID,PlayerID> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
// Forward the updated graph to all participants.
if (ignoreList.HasData(packet->playerId)==false)
ignoreList.Insert(packet->playerId,packet->playerId);
BroadcastGraphUpdate(ignoreList, peer);
}
void ConnectionGraph::OnNewConnection(RakPeerInterface *peer, Packet *packet)
{
// Only accept from participants
if (participantList.HasData(packet->playerId)==false)
return;
PlayerIdAndGroupId node1, node2;
unsigned short ping;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
inBitstream.Read(node1.playerId);
inBitstream.Read(node1.groupId);
inBitstream.Read(node2.playerId);
inBitstream.Read(node2.groupId);
if (inBitstream.Read(ping)==false)
return;
DataStructures::OrderedList<PlayerID,PlayerID> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
if (ignoreList.HasData(packet->playerId)==false)
ignoreList.Insert(packet->playerId,packet->playerId);
AddAndRelayConnection(ignoreList, node1, node2, ping, peer);
}
bool ConnectionGraph::OnConnectionLost(RakPeerInterface *peer, Packet *packet, unsigned char packetId)
{
// Only accept from participants
if (participantList.HasData(packet->playerId)==false)
return false;
PlayerID 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<PlayerID,PlayerID> ignoreList;
DeserializeIgnoreList(ignoreList, &inBitstream);
if (ignoreList.HasData(packet->playerId)==false)
ignoreList.Insert(packet->playerId, packet->playerId);
return RemoveAndRelayConnection(ignoreList, packetId, node1, node2, peer);
}
bool ConnectionGraph::DeserializeIgnoreList(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakNet::BitStream *inBitstream )
{
unsigned short count;
PlayerID 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);
}
return true;
}
void ConnectionGraph::SerializeWeightedGraph(RakNet::BitStream *out, const DataStructures::WeightedGraph<ConnectionGraph::PlayerIdAndGroupId, unsigned short, false> &g) const
{
unsigned nodeIndex, connectionIndex;
unsigned countOffset, oldOffset;
unsigned short count;
PlayerIdAndGroupId 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.playerId.port);
#endif
out->Write(node1.playerId);
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.playerId);
out->Write(node2.groupId);
out->Write(weight);
#ifdef _CONNECTION_GRAPH_DEBUG_PRINT
printf("(%i) ", node2.playerId.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;
PlayerIdAndGroupId node, connection;
bool anyConnectionsNew=false;
unsigned short weight;
inBitstream->ReadCompressed(nodeCount);
for (nodeIndex=0; nodeIndex < nodeCount; nodeIndex++)
{
inBitstream->Read(node.playerId);
inBitstream->Read(node.groupId);
inBitstream->AlignReadToByteBoundary();
if (inBitstream->Read(connectionCount)==false)
{
assert(0);
return false;
}
for (connectionIndex=0; connectionIndex < connectionCount; connectionIndex++)
{
inBitstream->Read(connection.playerId);
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.playerId!=UNASSIGNED_PLAYER_ID);
RakAssert(connection.playerId!=UNASSIGNED_PLAYER_ID);
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(PlayerID playerId)
{
unsigned index;
bool objectExists;
index=participantList.GetIndexFromKey(playerId, &objectExists);
if (objectExists)
participantList.RemoveAtIndex(index);
}
void ConnectionGraph::AddParticipant(PlayerID playerId)
{
if (participantList.HasData(playerId)==false)
participantList.Insert(playerId,playerId);
}
void ConnectionGraph::AddAndRelayConnection(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, const PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2, unsigned short ping, RakPeerInterface *peer)
{
if (graph.HasConnection(conn1,conn2))
return;
if (ping==65535)
ping=0;
assert(conn1.playerId!=UNASSIGNED_PLAYER_ID);
assert(conn2.playerId!=UNASSIGNED_PLAYER_ID);
graph.AddConnection(conn1,conn2,ping);
if (IsNewRemoteConnection(conn1,conn2,peer))
{
NotifyUserOfRemoteConnection(conn1,conn2,ping,peer);
return;
}
RakNet::BitStream outBitstream;
outBitstream.Write((unsigned char) ID_CONNECTION_GRAPH_NEW_CONNECTION);
outBitstream.Write(conn1.playerId);
outBitstream.Write(conn1.groupId);
outBitstream.Write(conn2.playerId);
outBitstream.Write(conn2.groupId);
outBitstream.Write(ping);
if (ignoreList.HasData(conn2.playerId)==false)
ignoreList.Insert(conn2.playerId,conn2.playerId);
if (ignoreList.HasData(conn1.playerId)==false)
ignoreList.Insert(conn1.playerId,conn1.playerId);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
}
bool ConnectionGraph::RemoveAndRelayConnection(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, unsigned char packetId, const PlayerID node1, const PlayerID node2, RakPeerInterface *peer)
{
PlayerIdAndGroupId n1, n2;
n1.playerId=node1;
n2.playerId=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);
if (ignoreList.HasData(node1)==false)
ignoreList.Insert(node1,node1);
if (ignoreList.HasData(node2)==false)
ignoreList.Insert(node2,node2);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
return true;
}
void ConnectionGraph::BroadcastGraphUpdate(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakPeerInterface *peer)
{
RakNet::BitStream outBitstream;
outBitstream.Write((unsigned char) ID_CONNECTION_GRAPH_UPDATE);
SerializeWeightedGraph(&outBitstream, graph);
SerializeIgnoreListAndBroadcast(&outBitstream, ignoreList, peer);
}
void ConnectionGraph::SerializeIgnoreListAndBroadcast(RakNet::BitStream *outBitstream, DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakPeerInterface *peer)
{
DataStructures::List<PlayerID> 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;
PlayerID self = peer->GetExternalID(sendList[0]);
if (ignoreList.HasData(self)==false)
ignoreList.Insert(self,self);
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 PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2,RakPeerInterface *peer)
{
if (graph.HasConnection(conn1,conn2)==false &&
subscribedGroups.HasData(conn1.groupId) &&
subscribedGroups.HasData(conn2.groupId) &&
(peer->GetIndexFromPlayerID(conn1.playerId)==-1 || peer->GetIndexFromPlayerID(conn2.playerId)==-1))
{
PlayerID externalId1, externalId2;
externalId1=peer->GetExternalID(conn1.playerId);
externalId2=peer->GetExternalID(conn2.playerId);
return (externalId1!=conn1.playerId && externalId1!=conn2.playerId &&
externalId2!=conn1.playerId && externalId2!=conn2.playerId);
}
return false;
}
void ConnectionGraph::NotifyUserOfRemoteConnection(const PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2,unsigned short ping, RakPeerInterface *peer)
{
// Create a packet to tell the user of this event
static const int length=sizeof(MessageID) + (sizeof(PlayerID) + sizeof(ConnectionGraphGroupID)) * 2 + sizeof(unsigned short);
Packet *p = AllocPacket(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.playerId);
b.Write(conn1.groupId);
b.Write(conn2.playerId);
b.Write(conn2.groupId);
b.Write(ping);
peer->PushBackPacket(p, false);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

165
raknet/ConnectionGraph.h Normal file
View File

@ -0,0 +1,165 @@
/// \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.rakkarsoft.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 "NetworkTypes.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();
~ConnectionGraph();
/// A node in the connection graph
struct RAK_DLL_EXPORT PlayerIdAndGroupId
{
PlayerIdAndGroupId();
~PlayerIdAndGroupId();
PlayerIdAndGroupId(PlayerID playerId, ConnectionGraphGroupID groupID);
PlayerID playerId;
ConnectionGraphGroupID groupId;
bool operator==( const PlayerIdAndGroupId& right ) const;
bool operator!=( const PlayerIdAndGroupId& right ) const;
bool operator > ( const PlayerIdAndGroupId& right ) const;
bool operator < ( const PlayerIdAndGroupId& 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::PlayerIdAndGroupId, 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 playerId is or should be a node on the connection graph
/// \param[in] peer The instance of RakPeer to send through
/// \param[in] playerId The system to send to
void RequestConnectionGraph(RakPeerInterface *peer, PlayerID playerId);
/// 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] playerId 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, PlayerID playerId, 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 OnDisconnect(RakPeerInterface *peer);
/// \internal
virtual void Update(RakPeerInterface *peer);
/// \internal
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal
virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId);
protected:
void HandleDroppedConnection(RakPeerInterface *peer, PlayerID playerId, unsigned char packetId);
void DeleteFromPeerList(PlayerID playerId);
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(PlayerID target, unsigned char packetId, RakPeerInterface *peer);
void SerializeWeightedGraph(RakNet::BitStream *out, const DataStructures::WeightedGraph<ConnectionGraph::PlayerIdAndGroupId, unsigned short, false> &g) const;
bool DeserializeWeightedGraph(RakNet::BitStream *inBitstream, RakPeerInterface *peer);
void AddAndRelayConnection(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, const PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2, unsigned short ping, RakPeerInterface *peer);
bool RemoveAndRelayConnection(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, unsigned char packetId, const PlayerID node1, const PlayerID node2, RakPeerInterface *peer);
void RemoveParticipant(PlayerID playerId);
void AddParticipant(PlayerID playerId);
void BroadcastGraphUpdate(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakPeerInterface *peer);
void NotifyUserOfRemoteConnection(const PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2,unsigned short ping, RakPeerInterface *peer);
bool IsNewRemoteConnection(const PlayerIdAndGroupId &conn1, const PlayerIdAndGroupId &conn2,RakPeerInterface *peer);
bool DeserializeIgnoreList(DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakNet::BitStream *inBitstream );
void SerializeIgnoreListAndBroadcast(RakNet::BitStream *outBitstream, DataStructures::OrderedList<PlayerID,PlayerID> &ignoreList, RakPeerInterface *peer);
RakNetTime nextWeightUpdate;
char *pw;
DataStructures::OrderedList<PlayerID, PlayerID> participantList;
DataStructures::WeightedGraph<ConnectionGraph::PlayerIdAndGroupId, 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

273
raknet/ConsoleServer.cpp Normal file
View File

@ -0,0 +1,273 @@
#include "ConsoleServer.h"
#include "TransportInterface.h"
#include "CommandParserInterface.h"
#include <string.h>
#include <stdlib.h>
#define COMMAND_DELINATOR ' '
#define COMMAND_DELINATOR_TOGGLE '"'
#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(_stricmp)
#define _stricmp strcasecmp
#endif
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.Del();
return;
}
}
}
void ConsoleServer::Update(void)
{
unsigned i;
char *parameterList[20]; // Up to 20 parameters
unsigned numParameters;
PlayerID newOrLostConnectionId;
Packet *p;
RegisteredCommand rc;
p = transport->Receive();
newOrLostConnectionId=transport->HasNewConnection();
if (newOrLostConnectionId!=UNASSIGNED_PLAYER_ID)
{
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_PLAYER_ID)
{
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 (_stricmp(*parameterList, "help")==0 && numParameters<=2)
{
// Find the parser specified and display help for it
if (numParameters==1)
{
transport->Send(p->playerId, "\r\nINSTRUCTIONS:\r\n");
transport->Send(p->playerId, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n");
transport->Send(p->playerId, "You can use quotation marks to toggle space delineation.\r\n");
transport->Send(p->playerId, "You can connect multiple times from the same computer.\r\n");
transport->Send(p->playerId, "You can direct commands to a parser by prefixing the parser name or number.\r\n");
transport->Send(p->playerId, "COMMANDS:\r\n");
transport->Send(p->playerId, "help Show this display.\r\n");
transport->Send(p->playerId, "help <ParserName> Show help on a particular parser.\r\n");
transport->Send(p->playerId, "help <CommandName> Show help on a particular command.\r\n");
transport->Send(p->playerId, "quit Disconnects from the server.\r\n");
transport->Send(p->playerId, "[<ParserName>] <Command> [<Parameters>] Execute a command\r\n");
transport->Send(p->playerId, "[<ParserNumber>] <Command> [<Parameters>] Execute a command\r\n");
ListParsers(p->playerId);
}
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->playerId);
transport->Send(p->playerId, "COMMAND LIST:\r\n");
commandParserList[i]->SendCommandList(transport, p->playerId);
transport->Send(p->playerId, "\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->playerId, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp);
else
transport->Send(p->playerId, "(%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->playerId, "Unknown help topic: %s.\r\n", parameterList[1]);
}
}
}
else if (_stricmp(*parameterList, "quit")==0 && numParameters==1)
{
transport->Send(p->playerId, "Goodbye!");
transport->CloseConnection(p->playerId);
}
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->playerId, "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->playerId, copy);
else
transport->Send(p->playerId, "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->playerId, copy);
else
transport->Send(p->playerId, "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->playerId, "Unknown command: Type 'help' for help.\r\n");
}
}
transport->DeallocatePacket(p);
p = transport->Receive();
}
}
void ConsoleServer::ListParsers(PlayerID playerId)
{
transport->Send(playerId,"INSTALLED PARSERS:\r\n");
unsigned i;
for (i=0; i < commandParserList.Size(); i++)
{
transport->Send(playerId, "%i. %s\r\n", i+1, commandParserList[i]->GetName());
}
}

62
raknet/ConsoleServer.h Normal file
View File

@ -0,0 +1,62 @@
/// \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.rakkarsoft.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 "DS_List.h"
#include "NetworkTypes.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:
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(PlayerID playerId);
TransportInterface *transport;
DataStructures::List<CommandParserInterface *> commandParserList;
char* password[256];
};
#endif

1157
raknet/DS_BPlusTree.h Normal file

File diff suppressed because it is too large Load Diff

1145
raknet/DS_BinarySearchTree.h Normal file

File diff suppressed because it is too large Load Diff

101
raknet/DS_ByteQueue.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "DS_ByteQueue.h"
#include <string.h> // Memmove
//#include <malloc.h> // PS3 doesn't have this
#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*)realloc(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)
free(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");
}

45
raknet/DS_ByteQueue.h Normal file
View File

@ -0,0 +1,45 @@
/// \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.rakkarsoft.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 "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:
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

250
raknet/DS_Heap.h Normal file
View File

@ -0,0 +1,250 @@
/// \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.rakkarsoft.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 "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:
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.Del();
#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.rakkarsoft.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++ )
delete [] 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, unsigned 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, unsigned sizeInBits, unsigned 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, unsigned 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,70 @@
/// \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.rakkarsoft.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 "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:
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, unsigned sizeInBytes, RakNet::BitStream * output );
// Decodes an array encoded by EncodeArray()
unsigned DecodeArray( RakNet::BitStream * input, unsigned sizeInBits, unsigned maxCharsToWrite, unsigned char *output );
void DecodeArray( unsigned char *input, unsigned 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,60 @@
/// \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.rakkarsoft.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
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:
/// 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.rakkarsoft.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

1258
raknet/DS_LinkedList.h Normal file

File diff suppressed because it is too large Load Diff

417
raknet/DS_List.h Normal file
View File

@ -0,0 +1,417 @@
/// \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.rakkarsoft.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.
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;
/// 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 the end of the list
void Del(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 );
/// \return The number of elements in the list
unsigned int Size( void ) const;
/// Clear the list
void Clear( bool doNotDeallocate=false );
/// 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
assert ( position < list_size );
#endif
return listArray[ position ];
}
template <class list_type>
void List<list_type>::Insert( const list_type input, const unsigned int position )
{
#ifdef _DEBUG
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 ];
// 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
assert( position < list_size );
#endif
if ( position < list_size )
{
// Compress the array
/*
for ( unsigned int counter = position; counter < list_size - 1 ; ++counter )
listArray[ counter ] = listArray[ counter + 1 ];
*/
memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type));
Del();
}
}
template <class list_type>
inline void List<list_type>::Del( 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 )
{
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 doNotDeallocate )
{
if ( allocation_size == 0 )
return;
if (allocation_size>512 && doNotDeallocate==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;
}
} // End namespace
#endif

314
raknet/DS_Map.h Normal file
View File

@ -0,0 +1,314 @@
/// \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.rakkarsoft.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:
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) {}
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);
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)));
}
}
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)));
}
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

90
raknet/DS_MemoryPool.h Normal file
View File

@ -0,0 +1,90 @@
#ifndef __MEMORY_POOL_H
#define __MEMORY_POOL_H
#include "DS_List.h"
#include <assert.h>
namespace DataStructures
{
template <class MemoryBlockType>
class MemoryPool
{
public:
MemoryPool();
~MemoryPool();
void Preallocate(unsigned numElements);
MemoryBlockType *Allocate(void);
void Release(MemoryBlockType *m);
void Clear(void);
protected:
int blocksOut;
DataStructures::List<MemoryBlockType*> pool;
};
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::MemoryPool()
{
#ifdef _DEBUG
blocksOut=0;
#endif
}
template<class MemoryBlockType>
MemoryPool<MemoryBlockType>::~MemoryPool()
{
#ifdef _DEBUG
assert(blocksOut==0);
#endif
unsigned i;
for (i=0; i < pool.Size(); i++)
delete pool[i];
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Preallocate(unsigned numElements)
{
unsigned i;
for (i=pool.Size(); i < numElements; i++)
{
pool.Insert(new MemoryBlockType);
}
}
template<class MemoryBlockType>
MemoryBlockType* MemoryPool<MemoryBlockType>::Allocate(void)
{
#ifdef _DEBUG
blocksOut++;
#endif
if (pool.Size()==0)
return new MemoryBlockType;
else
{
MemoryBlockType* out;
out=pool[pool.Size()-1];
pool.Del();
return out;
}
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Release(MemoryBlockType *m)
{
pool.Insert(m);
#ifdef _DEBUG
assert(blocksOut>0);
blocksOut--;
#endif
}
template<class MemoryBlockType>
void MemoryPool<MemoryBlockType>::Clear(void)
{
#ifdef _DEBUG
assert(blocksOut==0);
#endif
unsigned i;
for (i=0; i < pool.Size(); i++)
delete pool[i];
pool.Clear();
}
}
#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.rakkarsoft.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:
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.Del(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

236
raknet/DS_OrderedList.h Normal file
View File

@ -0,0 +1,236 @@
/// \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.rakkarsoft.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 "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 (*comparison_function)(const key_type&, const data_type&)=defaultOrderedListComparison<key_type, data_type> >
class RAK_DLL_EXPORT OrderedList
{
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) const;
unsigned GetIndexFromKey(const key_type &key, bool *objectExists) const;
data_type GetElementFromKey(const key_type &key);
unsigned Insert(const key_type &key, const data_type &data);
unsigned Remove(const key_type &key);
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 Del(const unsigned num=1);
void Clear(void);
unsigned Size(void) const;
protected:
DataStructures::List<data_type> orderedList;
};
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, comparison_function>::OrderedList()
{
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, comparison_function>::~OrderedList()
{
Clear();
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, comparison_function>::OrderedList( const OrderedList& original_copy )
{
orderedList=original_copy.orderedList;
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
OrderedList<key_type, data_type, comparison_function>& OrderedList<key_type, data_type, comparison_function>::operator= ( const OrderedList& original_copy )
{
orderedList=original_copy.orderedList;
return *this;
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
bool OrderedList<key_type, data_type, comparison_function>::HasData(const key_type &key) const
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists);
return objectExists;
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
data_type OrderedList<key_type, data_type, comparison_function>::GetElementFromKey(const key_type &key)
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists);
assert(objectExists);
return orderedList[index];
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, comparison_function>::GetIndexFromKey(const key_type &key, bool *objectExists) 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 = comparison_function(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 (*comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, comparison_function>::Insert(const key_type &key, const data_type &data)
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists);
// Don't allow duplicate insertion.
if (objectExists)
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 (*comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, comparison_function>::Remove(const key_type &key)
{
bool objectExists;
unsigned index;
index = GetIndexFromKey(key, &objectExists);
// Can't find the element to remove if this assert hits
assert(objectExists==true);
if (objectExists==false)
return 0;
orderedList.RemoveAtIndex(index);
return index;
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, comparison_function>::RemoveAtIndex(const unsigned index)
{
orderedList.RemoveAtIndex(index);
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, comparison_function>::InsertAtIndex(const data_type &data, const unsigned index)
{
orderedList.Insert(data, index);
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, comparison_function>::InsertAtEnd(const data_type &data)
{
orderedList.Insert(data);
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, comparison_function>::Del(const unsigned num)
{
orderedList.Del(num);
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
void OrderedList<key_type, data_type, comparison_function>::Clear(void)
{
orderedList.Clear();
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
data_type& OrderedList<key_type, data_type, comparison_function>::operator[]( const unsigned int position ) const
{
return orderedList[position];
}
template <class key_type, class data_type, int (*comparison_function)(const key_type&, const data_type&)>
unsigned OrderedList<key_type, data_type, comparison_function>::Size(void) const
{
return orderedList.Size();
}
}
#endif

415
raknet/DS_Queue.h Normal file
View File

@ -0,0 +1,415 @@
/// \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.rakkarsoft.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 Del( 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 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 )
{
if ( allocation_size == 0 )
{
array = new queue_type[ 16 ];
head = 0;
tail = 1;
array[ 0 ] = input;
allocation_size = 16;
return ;
}
if ( head == 0 )
head = allocation_size - 1;
else
--head;
unsigned count=0;
while (count < index)
{
array[head+count]=array[head+count+1];
count++;
}
array[ head+count ] = input;
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
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>
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>
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
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>::Del( 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

110
raknet/DS_QueueLinkedList.h Normal file
View File

@ -0,0 +1,110 @@
/// \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.rakkarsoft.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"
/// 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:
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

218
raknet/DS_RangeList.h Normal file
View File

@ -0,0 +1,218 @@
#ifndef __RANGE_LIST_H
#define __RANGE_LIST_H
#include "DS_OrderedList.h"
#include "BitStream.h"
#include <assert.h>
namespace DataStructures
{
template <class range_type>
struct RangeNode
{
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:
RangeList();
~RangeList();
void Insert(range_type index);
void Clear(void);
unsigned Size(void);
unsigned RangeSum(void);
unsigned Serialize(RakNet::BitStream *in, int maxBits, bool clearSerialized);
bool Deserialize(RakNet::BitStream *out);
DataStructures::OrderedList<range_type, RangeNode<range_type> , RangeNodeComp<range_type> > ranges;
};
template <class range_type>
unsigned RangeList<range_type>::Serialize(RakNet::BitStream *in, int maxBits, bool clearSerialized)
{
assert(ranges.Size() < (unsigned short)-1);
RakNet::BitStream tempBS;
int 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++;
}
int 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.Del(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;
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));
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));
}
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)
{
return ranges.Size();
}
template <class range_type>
unsigned RangeList<range_type>::RangeSum(void)
{
unsigned sum,i;
for (i=0; i < ranges.Size(); i++)
sum+=ranges[i].maxIndex-ranges[i].minIndex+1;
return sum;
}
}
#endif

772
raknet/DS_Table.cpp Normal file
View File

@ -0,0 +1,772 @@
#include "DS_Table.h"
#include "DS_OrderedList.h"
#include <string.h>
#include <assert.h>
using namespace DataStructures;
#ifdef _MSC_VER
#pragma warning( push )
#endif
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void ExtendRows(Table::Row* input, int index)
{
input->cells.Insert(new Table::Cell );
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void FreeRow(Table::Row* input, int index)
{
unsigned i;
for (i=0; i < input->cells.Size(); i++)
{
delete input->cells[i];
}
delete input;
}
Table::Cell::Cell()
{
isEmpty=true;
}
Table::Cell::~Cell()
{
Clear();
}
Table::Cell& Table::Cell::operator = ( const Table::Cell& input )
{
isEmpty=input.isEmpty;
i=input.i;
if (input.c)
{
if (c)
delete [] c;
c = new char [i];
memcpy(c, input.c, i);
}
return *this;
}
Table::Cell::Cell( const Table::Cell & input)
{
isEmpty=input.isEmpty;
i=input.i;
if (input.c)
{
if (c)
delete [] c;
c = new char [i];
memcpy(c, input.c, i);
}
}
void Table::Cell::Set(int input)
{
assert(isEmpty);
i=input;
c=0;
isEmpty=false;
}
void Table::Cell::Set(char *input)
{
assert(isEmpty);
if (input && input[0])
{
i=(int)strlen(input)+1;
c = new char [i];
strcpy(c, input);
}
else
c=0;
i=0;
isEmpty=false;
}
void Table::Cell::Set(char *input, int inputLength)
{
assert(isEmpty);
if (input)
{
c = new char [inputLength];
i=inputLength;
memcpy(c, input, inputLength);
}
else
{
c=0;
i=0;
}
isEmpty=false;
}
void Table::Cell::Get(int *output)
{
assert(isEmpty==false);
*output=i;
}
void Table::Cell::Get(char *output)
{
assert(isEmpty==false);
strcpy(output, c);
}
void Table::Cell::Get(char *output, int *outputLength)
{
assert(isEmpty==false);
memcpy(output, c, i);
if (outputLength)
*outputLength=i;
}
Table::Cell::Cell(int intValue, char *charValue, ColumnType type)
{
if (type==NUMERIC)
{
Set(intValue);
}
else if (type==STRING)
{
Set(charValue);
}
else
{
Set(charValue, intValue);
}
isEmpty=false;
}
void Table::Cell::Clear(void)
{
if (isEmpty==false)
delete [] c;
isEmpty=true;
}
Table::ColumnDescriptor::ColumnDescriptor()
{
}
Table::ColumnDescriptor::~ColumnDescriptor()
{
}
Table::ColumnDescriptor::ColumnDescriptor(char cn[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType ct)
{
columnType=ct;
strcpy(columnName, cn);
}
void Table::Row::UpdateCell(unsigned columnIndex, int value)
{
cells[columnIndex]->Clear();
cells[columnIndex]->Set(value);
// cells[columnIndex]->i=value;
// cells[columnIndex]->c=0;
// cells[columnIndex]->isEmpty=false;
}
void Table::Row::UpdateCell(unsigned columnIndex, char *str)
{
cells[columnIndex]->Clear();
cells[columnIndex]->Set(str);
}
void Table::Row::UpdateCell(unsigned columnIndex, int byteLength, char *data)
{
cells[columnIndex]->Clear();
cells[columnIndex]->Set(data,byteLength);
}
Table::Table()
{
}
Table::~Table()
{
Clear();
}
unsigned Table::AddColumn(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType)
{
if (columnName[0]==0)
return (unsigned) -1;
// Add this column.
columns.Insert(Table::ColumnDescriptor(columnName, columnType));
// Extend the rows by one
rows.ForEachData(ExtendRows);
return columns.Size()-1;
}
void Table::RemoveColumn(unsigned columnIndex)
{
if (columnIndex >= columns.Size())
return;
columns.RemoveAtIndex(columnIndex);
// Remove this index from each row.
int i;
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> *cur = rows.GetListHead();
while (cur)
{
for (i=0; i < cur->size; i++)
{
delete cur->data[i]->cells[columnIndex];
cur->data[i]->cells.RemoveAtIndex(columnIndex);
}
cur=cur->next;
}
}
unsigned Table::ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH])
{
unsigned columnIndex;
for (columnIndex=0; columnIndex<columns.Size(); columnIndex++)
if (strcmp(columnName, columns[columnIndex].columnName)==0)
return columnIndex;
return (unsigned)-1;
}
char* Table::ColumnName(unsigned index)
{
if (index >= columns.Size())
return 0;
else
return (char*)columns[index].columnName;
}
Table::ColumnType Table::GetColumnType(unsigned index)
{
if (index >= columns.Size())
return (Table::ColumnType) 0;
else
return columns[index].columnType;
}
unsigned Table::GetColumnCount(void) const
{
return columns.Size();
}
unsigned Table::GetRowCount(void) const
{
return rows.Size();
}
Table::Row* Table::AddRow(unsigned rowId)
{
Row *newRow;
newRow = new Row;
if (rows.Insert(rowId, newRow)==false)
{
delete newRow;
return 0; // Already exists
}
unsigned rowIndex;
for (rowIndex=0; rowIndex < columns.Size(); rowIndex++)
newRow->cells.Insert( new Table::Cell() );
return newRow;
}
Table::Row* Table::AddRow(unsigned rowId, DataStructures::List<Cell> &initialCellValues)
{
Row *newRow = new Row;
unsigned rowIndex;
for (rowIndex=0; rowIndex < columns.Size(); rowIndex++)
{
if (rowIndex < initialCellValues.Size() && initialCellValues[rowIndex].isEmpty==false)
newRow->cells.Insert(new Table::Cell(initialCellValues[rowIndex].i, initialCellValues[rowIndex].c, columns[rowIndex].columnType));
else
newRow->cells.Insert(new Table::Cell());
}
rows.Insert(rowId, newRow);
return newRow;
}
Table::Row* Table::AddRowColumns(unsigned rowId, Row *row, DataStructures::List<unsigned> columnIndices)
{
Row *newRow = new Row;
unsigned columnIndex;
for (columnIndex=0; columnIndex < columnIndices.Size(); columnIndex++)
{
if (row->cells[columnIndices[columnIndex]]->isEmpty==false)
{
newRow->cells.Insert(new Table::Cell(
row->cells[columnIndices[columnIndex]]->i,
row->cells[columnIndices[columnIndex]]->c,
columns[columnIndex].columnType
));
}
else
{
newRow->cells.Insert(new Cell());
}
}
rows.Insert(rowId, newRow);
return newRow;
}
void Table::RemoveRow(unsigned rowId)
{
Row *out;
if (rows.Delete(rowId, out))
DeleteRow(out);
}
void Table::RemoveRows(Table *tableContainingRowIDs)
{
unsigned i;
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> *cur = tableContainingRowIDs->GetRows().GetListHead();
while (cur)
{
for (i=0; i < (unsigned)cur->size; i++)
{
rows.Delete(cur->keys[i]);
}
cur=cur->next;
}
return;
}
bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int value)
{
assert(columns[columnIndex].columnType==NUMERIC);
Row *row = GetRowByID(rowId);
if (row)
{
row->UpdateCell(columnIndex, value);
return true;
}
return false;
}
bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, char *str)
{
assert(columns[columnIndex].columnType==STRING);
Row *row = GetRowByID(rowId);
if (row)
{
row->UpdateCell(columnIndex, str);
return true;
}
return false;
}
bool Table::UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data)
{
assert(columns[columnIndex].columnType==BINARY);
Row *row = GetRowByID(rowId);
if (row)
{
row->UpdateCell(columnIndex, byteLength, data);
return true;
}
return false;
}
Table::FilterQuery::FilterQuery()
{
}
Table::FilterQuery::~FilterQuery()
{
}
Table::FilterQuery::FilterQuery(unsigned column, Cell *cell, FilterQueryType op)
{
columnIndex=column;
cellValue=cell;
operation=op;
}
Table::Row* Table::GetRowByID(unsigned rowId)
{
Row *row;
if (rows.Get(rowId, row))
return row;
return 0;
}
Table::Row* Table::GetRowByIndex(unsigned rowIndex)
{
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> *cur = rows.GetListHead();
while (cur)
{
if (rowIndex < (unsigned)cur->size)
return cur->data[rowIndex];
if (rowIndex <= (unsigned)cur->size)
rowIndex-=cur->size;
else
return 0;
cur=cur->next;
}
return 0;
}
void Table::QueryTable(unsigned *columnSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result)
{
unsigned i;
DataStructures::List<unsigned> columnIndicesToReturn;
// Clear the result table.
result->Clear();
if (columnSubset && numColumnSubset>0)
{
for (i=0; i < numColumnSubset; i++)
{
if (columnSubset[i]>=0 && columnSubset[i]<columns.Size())
columnIndicesToReturn.Insert(columnSubset[i]);
}
}
else
{
for (i=0; i < columns.Size(); i++)
columnIndicesToReturn.Insert(i);
}
if (columnIndicesToReturn.Size()==0)
return; // No valid columns specified
for (i=0; i < columnIndicesToReturn.Size(); i++)
{
result->AddColumn(columns[columnIndicesToReturn[i]].columnName,columns[columnIndicesToReturn[i]].columnType);
}
// Get the column indices of the filter queries.
DataStructures::List<unsigned> inclusionFilterColumnIndices;
if (inclusionFilters && numInclusionFilters>0)
{
for (i=0; i < numInclusionFilters; i++)
{
if (inclusionFilters[i].columnIndex>=0 && inclusionFilters[i].columnIndex<columns.Size())
inclusionFilterColumnIndices.Insert(inclusionFilters[i].columnIndex);
else
inclusionFilterColumnIndices.Insert((unsigned)-1);
}
}
if (rowIds==0 || numRowIDs==0)
{
// All rows
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> *cur = rows.GetListHead();
while (cur)
{
for (i=0; i < (unsigned)cur->size; i++)
{
QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, cur->keys[i], cur->data[i], inclusionFilters, result);
}
cur=cur->next;
}
}
else
{
// Specific rows
Row *row;
for (i=0; i < numRowIDs; i++)
{
if (rows.Get(rowIds[i], row))
{
QueryRow(inclusionFilterColumnIndices, columnIndicesToReturn, rowIds[i], row, inclusionFilters, result);
}
}
}
}
void Table::QueryRow(DataStructures::List<unsigned> &inclusionFilterColumnIndices, DataStructures::List<unsigned> &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result)
{
bool pass;
unsigned columnIndex;
unsigned j;
// If no inclusion filters, just add the row
if (inclusionFilterColumnIndices.Size()==0)
{
result->AddRowColumns(key, row, columnIndicesToReturn);
}
else
{
// Go through all inclusion filters. Only add this row if all filters pass.
for (j=0; j<inclusionFilterColumnIndices.Size(); j++)
{
columnIndex=inclusionFilterColumnIndices[j];
if (row->cells[columnIndex]->isEmpty==false && columnIndex!=(unsigned)-1)
{
switch (inclusionFilters[j].operation)
{
case QF_EQUAL:
switch(columns[inclusionFilterColumnIndices[j]].columnType)
{
case NUMERIC:
pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i;
break;
case STRING:
pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)==0;
break;
case BINARY:
pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i &&
memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, row->cells[columnIndex]->i)==0;
break;
}
break;
case QF_NOT_EQUAL:
switch(columns[inclusionFilterColumnIndices[j]].columnType)
{
case NUMERIC:
pass=row->cells[columnIndex]->i!=inclusionFilters[j].cellValue->i;
break;
case STRING:
pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)!=0;
break;
case BINARY:
pass=row->cells[columnIndex]->i==inclusionFilters[j].cellValue->i &&
memcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c, row->cells[columnIndex]->i)==0;
break;
}
break;
case QF_GREATER_THAN:
switch(columns[inclusionFilterColumnIndices[j]].columnType)
{
case NUMERIC:
pass=row->cells[columnIndex]->i>inclusionFilters[j].cellValue->i;
break;
case STRING:
pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)>0;
break;
}
break;
case QF_LESS_THAN:
switch(columns[inclusionFilterColumnIndices[j]].columnType)
{
case NUMERIC:
pass=row->cells[columnIndex]->i<inclusionFilters[j].cellValue->i;
break;
case STRING:
pass=strcmp(row->cells[columnIndex]->c,inclusionFilters[j].cellValue->c)<0;
break;
}
break;
case QF_IS_EMPTY:
pass=false;
break;
case QF_NOT_EMPTY:
pass=true;
break;
}
}
else
{
if (inclusionFilters[j].operation==QF_IS_EMPTY)
pass=true;
else
pass=false; // No value for this cell
}
if (pass==false)
break;
}
if (pass)
{
result->AddRowColumns(key, row, columnIndicesToReturn);
}
}
}
static Table::SortQuery *_sortQueries;
static unsigned _numSortQueries;
static DataStructures::List<unsigned> *_columnIndices;
static DataStructures::List<Table::ColumnDescriptor> *_columns;
int RowSort(Table::Row* const &first, Table::Row* const &second) // first is the one inserting, second is the one already there.
{
unsigned i, columnIndex;
for (i=0; i<_numSortQueries; i++)
{
columnIndex=(*_columnIndices)[i];
if (columnIndex==(unsigned)-1)
continue;
if (first->cells[columnIndex]->isEmpty==true && second->cells[columnIndex]->isEmpty==false)
return 1; // Empty cells always go at the end
if (first->cells[columnIndex]->isEmpty==false && second->cells[columnIndex]->isEmpty==true)
return -1; // Empty cells always go at the end
if (_sortQueries[i].operation==Table::QS_INCREASING_ORDER)
{
if ((*_columns)[columnIndex].columnType==Table::NUMERIC)
{
if (first->cells[columnIndex]->i>second->cells[columnIndex]->i)
return 1;
if (first->cells[columnIndex]->i<second->cells[columnIndex]->i)
return -1;
}
else
{
// String
if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0)
return 1;
if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0)
return -1;
}
}
else
{
if ((*_columns)[columnIndex].columnType==Table::NUMERIC)
{
if (first->cells[columnIndex]->i<second->cells[columnIndex]->i)
return 1;
if (first->cells[columnIndex]->i>second->cells[columnIndex]->i)
return -1;
}
else
{
// String
if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)<0)
return 1;
if (strcmp(first->cells[columnIndex]->c,second->cells[columnIndex]->c)>0)
return -1;
}
}
}
return 0;
}
void Table::SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out)
{
unsigned i;
unsigned outLength;
DataStructures::List<unsigned> columnIndices;
_sortQueries=sortQueries;
_numSortQueries=numSortQueries;
_columnIndices=&columnIndices;
_columns=&columns;
bool anyValid=false;
for (i=0; i < numSortQueries; i++)
{
if (sortQueries[i].columnIndex>=0 && sortQueries[i].columnIndex<columns.Size() && columns[sortQueries[i].columnIndex].columnType!=BINARY)
{
columnIndices.Insert(sortQueries[i].columnIndex);
anyValid=true;
}
else
columnIndices.Insert((unsigned)-1); // Means don't check this column
}
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> *cur;
cur = rows.GetListHead();
if (anyValid==false)
{
outLength=0;
while (cur)
{
for (i=0; i < (unsigned)cur->size; i++)
{
out[(outLength)++]=cur->data[i];
}
cur=cur->next;
}
return;
}
// Start adding to ordered list.
DataStructures::OrderedList<Row*, Row*, RowSort> orderedList;
while (cur)
{
for (i=0; i < (unsigned)cur->size; i++)
{
orderedList.Insert(cur->data[i],cur->data[i]);
}
cur=cur->next;
}
outLength=0;
for (i=0; i < orderedList.Size(); i++)
out[(outLength)++]=orderedList[i];
}
void Table::PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow)
{
if (outLength<=0)
return;
if (outLength==1)
{
*out=0;
return;
}
if (inputRow->cells.Size()!=columns.Size())
{
strncpy(out, "Cell width does not match column width.\n", outLength);
out[outLength-1]=0;
return;
}
char buff[512];
unsigned i;
int len;
out[0]=0;
for (i=0; i < columns.Size(); i++)
{
if (columns[i].columnType==NUMERIC)
{
if (inputRow->cells[i]->isEmpty==false)
{
sprintf(buff, "%i", inputRow->cells[i]->i);
len=(int)strlen(buff);
}
else
len=0;
if (i+1!=columns.Size())
buff[len++]=columnDelineator;
buff[len]=0;
}
else if (columns[i].columnType==STRING)
{
if (inputRow->cells[i]->isEmpty==false && inputRow->cells[i]->c)
{
strncpy(buff, inputRow->cells[i]->c, 512-2);
buff[512-2]=0;
len=(int)strlen(buff);
}
else
len=0;
if (i+1!=columns.Size())
buff[len++]=columnDelineator;
buff[len]=0;
}
else
{
if (printDelineatorForBinary)
{
if (i+1!=columns.Size())
buff[0]=columnDelineator;
buff[1]=0;
}
else
buff[0]=0;
}
len=(int)strlen(out);
if (outLength==len+1)
break;
strncpy(out+len, buff, outLength-len);
out[outLength-1]=0;
}
}
void Table::Clear(void)
{
rows.ForEachData(FreeRow);
rows.Clear();
columns.Clear(true);
}
List<Table::ColumnDescriptor>& Table::GetColumns(void)
{
return columns;
}
DataStructures::BPlusTree<unsigned, Table::Row*, _TABLE_BPLUS_TREE_ORDER>& Table::GetRows(void)
{
return rows;
}
DataStructures::Page<unsigned, DataStructures::Table::Row*, _TABLE_BPLUS_TREE_ORDER> * Table::GetListHead(void)
{
return rows.GetListHead();
}
void Table::DeleteRow(Table::Row *row)
{
unsigned rowIndex;
for (rowIndex=0; rowIndex < row->cells.Size(); rowIndex++)
{
delete row->cells[rowIndex];
}
delete row;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

285
raknet/DS_Table.h Normal file
View File

@ -0,0 +1,285 @@
#ifndef __TABLE_H
#define __TABLE_H
#ifdef _MSC_VER
#pragma warning( push )
#endif
#include "DS_List.h"
#include "DS_BPlusTree.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 Table
{
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 bits.
BINARY
};
/// Holds the actual data in the table
struct Cell
{
Cell();
~Cell();
Cell(int intValue, char *charValue, ColumnType type);
void Clear(void);
/// Numeric
void Set(int input);
/// String
void Set(char *input);
/// Binary
void Set(char *input, int inputLength);
/// Numeric
void Get(int *output);
/// String
void Get(char *output);
/// Binary
void Get(char *output, int *outputLength);
// assignment operator and copy constructor
Cell& operator = ( const Cell& input );
Cell( const Cell & input);
bool isEmpty;
int i;
char *c;
};
/// Stores the name and type of the column
/// \internal
struct ColumnDescriptor
{
ColumnDescriptor();
~ColumnDescriptor();
ColumnDescriptor(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 Row
{
// list of cells
DataStructures::List<Cell*> cells;
/// Numeric
void UpdateCell(unsigned columnIndex, int value);
/// String
void UpdateCell(unsigned columnIndex, char *str);
/// Binary
void UpdateCell(unsigned columnIndex, int byteLength, char *data);
};
// Operations to perform for cell comparison
enum FilterQueryType
{
QF_EQUAL,
QF_NOT_EQUAL,
QF_GREATER_THAN,
QF_LESS_THAN,
QF_IS_EMPTY,
QF_NOT_EMPTY,
};
// Compare the cell value for a row at columnName to the cellValue using operation.
struct FilterQuery
{
FilterQuery();
~FilterQuery();
FilterQuery(unsigned column, Cell *cell, FilterQueryType op);
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 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(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]);
/// \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);
/// 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);
/// Gets a row at a specific index
/// rowIndex should be less than GetRowCount()
/// \param[in] rowIndex The index of the row
/// \return The desired row, or 0 if no such row.
Row* GetRowByIndex(unsigned rowIndex);
/// \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 *columnSubset, 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);
/// 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);
/// 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);
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

97
raknet/DS_Tree.h Normal file
View File

@ -0,0 +1,97 @@
/// \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.rakkarsoft.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"
/// 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:
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

537
raknet/DS_WeightedGraph.h Normal file
View File

@ -0,0 +1,537 @@
/// \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.rakkarsoft.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>
#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:
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 isValid;
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()
{
isValid=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;
isValid=original_copy.isValid;
if (isValid)
{
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;
isValid=original_copy.isValid;
if (isValid)
{
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 (isValid==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)
{
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));
}
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
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
isValid=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
*/
isValid=true;
}
template <class node_type, class weight_type, bool allow_unlinkedNodes>
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::ClearDijkstra(void)
{
if (isValid)
{
isValid=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).playerId.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).playerId.ToString(), (float) adjacencyLists[i]->operator[](j) );
}
printf("\n");
}
#endif
}
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif
#endif

View File

@ -1,8 +1,207 @@
// TODO: Implement DataBlockEncryptor.cpp
/// \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.rakkarsoft.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, int inputLength, unsigned char *output, 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, int inputLength, unsigned char *output, 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; ( int ) index <= inputLength - 16;index += 16 )
{
// secretKeyAES128.decrypt16( input + index );
blockDecrypt(&cipherInst, &keyDecrypt, input + index, 16, input + index);
for ( byteIndex = 0; byteIndex < 16; byteIndex++ )
{
if ( index + 16 == ( unsigned ) inputLength )
input[ index + byteIndex ] ^= input[ byteIndex ];
else
input[ index + byteIndex ] ^= input[ index + 16 + byteIndex ];
}
lastBlock = index;
};
// Decrypt the first block
//secretKeyAES128.decrypt16( input );
blockDecrypt(&cipherInst, &keyDecrypt, input, 16, input);
// Read checksum
#ifdef HOST_ENDIAN_IS_BIG
checkSum = (unsigned int)input[0] | (unsigned int)(input[1]<<8) |
(unsigned int)(input[2]<<16)|(unsigned int)(input[3]<<24);
#else
memcpy( ( char* ) & checkSum, input, sizeof( checkSum ) );
#endif
// Read the pad size variable
memcpy( ( char* ) & encodedPad, input + 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( input + sizeof( checkSum ), *outputLength + sizeof( randomChar ) + sizeof( encodedPad ) + paddingBytes );
if ( checkSum != checkSumCalculator.Get() )
return false;
// Read the data
if ( input == output )
memmove( output, input + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength );
else
memcpy( output, input + sizeof( randomChar ) + sizeof( checkSum ) + sizeof( encodedPad ) + paddingBytes, *outputLength );
return true;
}

View File

@ -1,8 +1,25 @@
// TODO: Implement DataBlockEncryptor.h
/// \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.rakkarsoft.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"
/// Encrypts and decrypts data blocks.
class DataBlockEncryptor
{
@ -12,7 +29,41 @@ 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 up to 15 bytes. 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, int inputLength, unsigned char *output, 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, int inputLength, unsigned char *output, int *outputLength );
protected:
keyInstance keyEncrypt;
keyInstance keyDecrypt;
cipherInstance cipherInst;
bool keySet;
};

58
raknet/DataCompressor.cpp Normal file
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 the PS3
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;
unsigned int 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 = new unsigned char [destinationSizeInBytes];
tree.GenerateFromFrequencyTable(frequencyTable);
decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output );
assert(decompressedBytes==destinationSizeInBytes);
return destinationSizeInBytes;
}

33
raknet/DataCompressor.h Normal file
View File

@ -0,0 +1,33 @@
/// \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.rakkarsoft.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 "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:
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,227 @@
#include "DirectoryDeltaTransfer.h"
#include "FileList.h"
#include "StringCompressor.h"
#include "RakPeerInterface.h"
#include "FileListTransfer.h"
#include "FileListTransferCBInterface.h"
#include "BitStream.h"
#include "PacketEnumerations.h"
#include "FileOperations.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
class DDTCallback : public FileListTransferCBInterface
{
public:
unsigned subdirLen;
char outputSubdir[512];
FileListTransferCBInterface *onFileCallback;
virtual void OnFile(
unsigned fileIndex,
char *filename,
char *fileData,
unsigned compressedTransmissionLength,
unsigned finalDataLength,
unsigned short setID,
unsigned setCount,
unsigned setTotalCompressedTransmissionLength,
unsigned setTotalFinalLength,
unsigned char context)
{
char fullPathToDir[1024];
if (filename && fileData && subdirLen < strlen(filename))
{
strcpy(fullPathToDir, outputSubdir);
strcat(fullPathToDir, filename+subdirLen);
WriteFileWithDirectories(fullPathToDir, (char*)fileData, finalDataLength);
}
else
fullPathToDir[0]=0;
onFileCallback->OnFile(fileIndex, fullPathToDir, fileData, compressedTransmissionLength, finalDataLength, setID, setCount, setTotalCompressedTransmissionLength, setTotalFinalLength, context);
}
virtual void OnFileProgress(unsigned fileIndex,
char *filename,
unsigned compressedTransmissionLength,
unsigned finalDataLength,
unsigned short setID,
unsigned setCount,
unsigned setTotalCompressedTransmissionLength,
unsigned setTotalFinalLength,
unsigned char context,
unsigned int partCount,
unsigned int partTotal,
unsigned int partLength)
{
char fullPathToDir[1024];
if (filename && subdirLen < strlen(filename))
{
strcpy(fullPathToDir, outputSubdir);
strcat(fullPathToDir, filename+subdirLen);
}
else
fullPathToDir[0]=0;
onFileCallback->OnFileProgress(fileIndex, fullPathToDir, compressedTransmissionLength, finalDataLength, setID, setCount, setTotalCompressedTransmissionLength, setTotalFinalLength, context, partCount, partTotal, partLength);
}
};
DirectoryDeltaTransfer::DirectoryDeltaTransfer()
{
applicationDirectory[0]=0;
fileListTransfer=0;
availableUploads = new FileList;
rakPeer=0;
priority=HIGH_PRIORITY;
orderingChannel=0;
}
DirectoryDeltaTransfer::~DirectoryDeltaTransfer()
{
delete availableUploads;
}
void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt)
{
fileListTransfer=flt;
}
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, PlayerID host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel)
{
if (rakPeer->GetIndexFromPlayerID(host)==-1)
return (unsigned short) -1;
DDTCallback *transferCallback;
FileList localFiles;
// 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((unsigned char) 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();
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void DirectoryDeltaTransfer::OnDownloadRequest(RakPeerInterface *peer, Packet *packet)
{
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->playerId, setId, priority, orderingChannel, true);
}
void DirectoryDeltaTransfer::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void DirectoryDeltaTransfer::Update(RakPeerInterface *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;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void DirectoryDeltaTransfer::OnDisconnect(RakPeerInterface *peer)
{
}
unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const
{
return availableUploads->fileList.Size();
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@ -0,0 +1,124 @@
/// \file
/// \brief Simple class to send changes between directories. In essense, 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.rakkarsoft.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 "NetworkTypes.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;
/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer
/// \ingroup PLUGINS_GROUP
/// \brief Simple class to send changes between directories. In essense, 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
~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()
/// \return A set ID, identifying this download set. Returns 65535 on host unreachable.
unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, PlayerID host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel);
/// 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;
/// \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 OnDisconnect(RakPeerInterface *peer);
protected:
void OnDownloadRequest(RakPeerInterface *peer, Packet *packet);
char applicationDirectory[512];
FileListTransfer *fileListTransfer;
FileList *availableUploads;
RakPeerInterface *rakPeer;
PacketPriority priority;
char orderingChannel;
};
#endif

View File

@ -1,13 +1,250 @@
// TODO: Implement EmailSender.cpp
// 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 <stdio.h>
#include <string.h> // strstr
#if defined(_COMPATIBILITY_1)
#include "Compatibility1Includes.h"
#endif
#include "RakSleep.h"
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)
{
Packet *packet;
char query[1024];
char *response;
TCPInterface tcpInterface;
PlayerID emailServer;
if (tcpInterface.Start(0, 0)==false)
return "Unknown error starting TCP";
emailServer=tcpInterface.Connect(hostAddress, hostPort);
if (emailServer==UNASSIGNED_PLAYER_ID)
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("HELO\r\n", 6, 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);
response=GetResponse(&tcpInterface, emailServer, doPrintf);
if (response!=0)
return response;
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 char base64Map[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
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 = new char [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);
delete [] 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].dataLength, attachedFiles->fileList[i].filename, attachedFiles->fileList[i].filename);
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer);
newBody = new char[(attachedFiles->fileList[i].dataLength*3)/2];
outputOffset=Base64Encoding(attachedFiles->fileList[i].data, attachedFiles->fileList[i].dataLength, newBody, base64Map);
// Send the base64 mapped file.
tcpInterface.Send(newBody, outputOffset, emailServer);
delete [] 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
}
char *EmailSender::GetResponse(TCPInterface *tcpInterface, const PlayerID &emailServer, bool doPrintf)
{
Packet *packet;
@ -36,3 +273,70 @@ char *EmailSender::GetResponse(TCPInterface *tcpInterface, const PlayerID &email
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;
}

View File

@ -1,14 +1,47 @@
// TODO: Implement EmailSender.h
/// \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.rakkarsoft.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 "NetworkTypes.h"
/// \brief Rudimentary class to send email from code.
class EmailSender
{
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?)
/// \return 0 on success, otherwise a string indicating the error message
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);
// Returns how many bytes were written
int Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map);
protected:
char *GetResponse(TCPInterface *tcpInterface, const PlayerID &emailServer, bool doPrintf);
};

View File

@ -0,0 +1 @@

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.rakkarsoft.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.rakkarsoft.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
*/

592
raknet/FileList.cpp Normal file
View File

@ -0,0 +1,592 @@
#include "FileList.h"
#include <assert.h>
#ifndef _COMPATIBILITY_2
#if defined(_WIN32) || defined(__CYGWIN__)
#include <io.h>
#elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ )
#include <sys/io.h>
#endif
#endif
#include <stdio.h>
#include "DS_Queue.h"
#ifdef _WIN32
// For mkdir
#include <direct.h>
#else
#include <sys/stat.h>
#include "LinuxStrings.h"
#endif
#include "SHA1.h"
#include "StringCompressor.h"
#include "BitStream.h"
#include "FileOperations.h"
#if !defined(_WIN32)
#define _unlink unlink
#define _mkdir mkdir
#endif
// alloca
#ifdef _COMPATIBILITY_1
#elif defined(_WIN32)
#include <malloc.h>
#elif defined(_COMPATIBILITY_2)
#include "Compatibility2Includes.h"
#else
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "LinuxStrings.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);
//}
FileList::FileList()
{
}
FileList::~FileList()
{
Clear();
}
void FileList::AddFile(const char *filepath, const char *filename, unsigned char context)
{
if (filepath==0 || filename==0)
return;
char *data;
FILE *fp = fopen(filepath, "rb");
if (fp==0)
return;
fseek(fp, 0, SEEK_END);
int length = ftell(fp);
fseek(fp, 0, SEEK_SET);
#if !defined(_COMPATIBILITY_1)
bool usedAlloca=false;
if (length < MAX_ALLOCA_STACK_ALLOCATION)
{
data = ( char* ) alloca( length );
usedAlloca=true;
}
else
#endif
{
data = new char [length];
}
AddFile(filename, data, length, length, context);
fclose(fp);
#if !defined(_COMPATIBILITY_1)
if (usedAlloca==false)
#endif
delete [] data;
}
void FileList::AddFile(const char *filename, const char *data, const unsigned dataLength, const unsigned fileLength, unsigned char context)
{
if (filename==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].fileLength==fileLength && fileList[i].dataLength==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
delete [] fileList[i].data;
delete [] fileList[i].filename;
fileList.RemoveAtIndex(i);
break;
}
}
FileListNode n;
n.filename=new char [strlen(filename)+1];
if (dataLength)
{
n.data=new char [dataLength];
memcpy(n.data, data, dataLength);
}
else
n.data=0;
n.dataLength=dataLength;
n.fileLength=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)
{
#ifndef _COMPATIBILITY_2
DataStructures::Queue<char*> dirList;
char root[260];
char fullPath[520];
_finddata_t fileInfo;
intptr_t dir;
int file;
FILE *fp;
CSHA1 sha1;
char *dirSoFar, *fileData;
dirSoFar=new char[520];
if (applicationDirectory)
strcpy(root, applicationDirectory);
else
root[0]=0;
int rootLen=(int)strlen(root);
if (rootLen)
{
strcpy(dirSoFar, root);
if (dirSoFar[strlen(dirSoFar)-1]!='/' && dirSoFar[strlen(dirSoFar)-1]!='\\')
{
strcat(dirSoFar, "/");
rootLen++;
}
}
else
dirSoFar[0]=0;
if (subDirectory)
{
strcat(dirSoFar, subDirectory);
if (dirSoFar[strlen(dirSoFar)-1]!='/' && dirSoFar[strlen(dirSoFar)-1]!='\\')
{
strcat(dirSoFar, "/");
}
}
dirList.Push(dirSoFar);
while (dirList.Size())
{
dirSoFar=dirList.Pop();
strcpy(fullPath, dirSoFar);
strcat(fullPath, "*.*");
dir=_findfirst(fullPath, &fileInfo ); // Read .
if (dir==-1)
{
_findclose(dir);
delete [] dirSoFar;
unsigned i;
for (i=0; i < dirList.Size(); i++)
delete [] dirList[i];
return;
}
file=_findnext(dir, &fileInfo ); // Read ..
file=_findnext(dir, &fileInfo ); // Skip ..
while (file!=-1)
{
if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0)
{
strcpy(fullPath, dirSoFar);
strcat(fullPath, fileInfo.name);
if (writeData && writeHash)
fileData= new char [fileInfo.size+SHA1_LENGTH];
else
fileData= new char [fileInfo.size];
fp = fopen(fullPath, "rb");
if (writeData && writeHash)
fread(fileData+SHA1_LENGTH, fileInfo.size, 1, fp);
else
fread(fileData, fileInfo.size, 1, fp);
fclose(fp);
if (writeData && writeHash)
{
sha1.Reset();
sha1.Update( ( unsigned char* ) fileData+SHA1_LENGTH, fileInfo.size );
sha1.Final();
memcpy(fileData, sha1.GetHash(), SHA1_LENGTH);
AddFile((const char*)fullPath+rootLen, fileData, fileInfo.size+SHA1_LENGTH, fileInfo.size, context);
}
else if (writeHash)
{
sha1.Reset();
sha1.Update( ( unsigned char* ) fileData, fileInfo.size );
sha1.Final();
AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), SHA1_LENGTH, fileInfo.size, context);
}
else if (writeData)
{
AddFile(fullPath+rootLen, fileData, fileInfo.size, fileInfo.size, context);
}
else
AddFile(fullPath+rootLen, 0, 0, fileInfo.size, context);
delete [] fileData;
}
else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive)
{
char *newDir=new char[520];
strcpy(newDir, dirSoFar);
strcat(newDir, fileInfo.name);
strcat(newDir, "/");
dirList.Push(newDir);
}
file=_findnext(dir, &fileInfo );
}
_findclose(dir);
delete [] dirSoFar;
}
#endif
}
void FileList::Clear(void)
{
unsigned i;
for (i=0; i<fileList.Size(); i++)
{
delete [] fileList[i].data;
delete [] 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, 512, outBitStream);
outBitStream->Write((bool)(fileList[i].dataLength>0==true));
if (fileList[i].dataLength>0)
{
outBitStream->WriteCompressed(fileList[i].dataLength);
outBitStream->Write(fileList[i].data, fileList[i].dataLength);
}
outBitStream->Write((bool)(fileList[i].fileLength==fileList[i].dataLength));
if (fileList[i].fileLength!=fileList[i].dataLength)
outBitStream->WriteCompressed(fileList[i].fileLength);
}
}
bool FileList::Deserialize(RakNet::BitStream *inBitStream)
{
bool b, dataLenNonZero, fileLenMatchesDataLen;
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, 512, inBitStream);
inBitStream->Read(dataLenNonZero);
if (dataLenNonZero)
{
inBitStream->ReadCompressed(n.dataLength);
// sanity check
if (n.dataLength>200000000)
{
#ifdef _DEBUG
assert(n.dataLength<=200000000);
#endif
return false;
}
n.data=new char [n.dataLength];
inBitStream->Read(n.data, n.dataLength);
}
else
{
n.dataLength=0;
n.data=0;
}
b=inBitStream->Read(fileLenMatchesDataLen);
if (fileLenMatchesDataLen)
n.fileLength=n.dataLength;
else
b=inBitStream->ReadCompressed(n.fileLength);
#ifdef _DEBUG
assert(b);
#endif
if (b==0)
{
Clear();
return false;
}
n.filename=new char [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].fileLength==fileList[thisIndex].fileLength &&
input->fileList[inputIndex].dataLength==fileList[thisIndex].dataLength &&
memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,fileList[thisIndex].dataLength)==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[inputIndex].filename, 0,0, fileList[inputIndex].fileLength, 0);
break;
}
}
}
if (match==false)
{
// Other system does not have the file at all
output->AddFile(fileList[thisIndex].filename, 0,0, fileList[thisIndex].fileLength, 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);
if (fullPath[strlen(fullPath)-1]!='/' && fullPath[strlen(fullPath)-1]!='\\')
strcat(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].fileLength && alwaysWriteHash==false)
{
missingOrChangedFiles->AddFile(fileList[i].filename, 0, 0, fileLength, 0);
}
else
{
fileData= new char [fileLength];
fread(fileData, fileLength, 1, fp);
fclose(fp);
sha1.Reset();
sha1.Update( ( unsigned char* ) fileData, fileLength );
sha1.Final();
delete [] fileData;
if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, 20)!=0)
{
if (neverWriteHash==false)
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), 20, fileLength, 0);
else
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, 0, 0, fileLength, 0);
}
}
}
}
}
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())
{
delete [] fileList[i].data;
strcpy(fullPath, applicationDirectory);
if (fullPath[strlen(fullPath)-1]!='/' && fullPath[strlen(fullPath)-1]!='\\')
strcat(fullPath, "/");
strcat(fullPath,fileList[i].filename);
fp=fopen(fullPath, "rb");
if (fp)
{
if (writeFileHash || writeFileData)
{
fseek(fp, 0, SEEK_END);
fileList[i].fileLength = ftell(fp);
fseek(fp, 0, SEEK_SET);
if (writeFileHash)
{
if (writeFileData)
{
// Hash + data so offset the data by SHA1_LENGTH
fileList[i].data=new char[fileList[i].fileLength+SHA1_LENGTH];
fread(fileList[i].data+SHA1_LENGTH, fileList[i].fileLength, 1, fp);
sha1.Reset();
sha1.Update((unsigned char*)fileList[i].data+SHA1_LENGTH, fileList[i].fileLength);
sha1.Final();
memcpy(fileList[i].data, sha1.GetHash(), SHA1_LENGTH);
}
else
{
// Hash only
fileList[i].dataLength=SHA1_LENGTH;
if (fileList[i].fileLength < SHA1_LENGTH)
fileList[i].data=new char[SHA1_LENGTH];
else
fileList[i].data=new char[fileList[i].fileLength];
fread(fileList[i].data, fileList[i].fileLength, 1, fp);
sha1.Reset();
sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength);
sha1.Final();
memcpy(fileList[i].data, sha1.GetHash(), SHA1_LENGTH);
}
}
else
{
// Data only
fileList[i].dataLength=fileList[i].fileLength;
fileList[i].data=new char[fileList[i].fileLength];
fread(fileList[i].data, fileList[i].fileLength, 1, fp);
}
fclose(fp);
i++;
}
else
{
fileList[i].data=0;
fileList[i].dataLength=0;
}
}
else
{
if (removeUnknownFiles)
{
delete [] 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);
if (fullPath[strlen(fullPath)-1]!='/' && fullPath[strlen(fullPath)-1]!='\\')
strcat(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, fileList[i].dataLength);
}
}
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);
strcat(fullPath, fileList[i].filename);
_unlink(fullPath);
}
}

51
raknet/FileList.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef __FILE_LIST
#define __FILE_LIST
#include "Export.h"
#include "DS_List.h"
namespace RakNet
{
class BitStream;
}
struct FileListNode
{
char *filename;
char *data;
unsigned dataLength;
unsigned fileLength;
unsigned char context; // User specific data for whatever, describing this file.
};
//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data );
class RakPeerInterface;
class RAK_DLL_EXPORT FileList
{
public:
FileList();
~FileList();
void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, unsigned char context);
void Clear(void);
void Serialize(RakNet::BitStream *outBitStream);
bool Deserialize(RakNet::BitStream *inBitStream);
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 FileList.
// Specify dirSubset to only consider files in fileList 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.
void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir);
void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles);
void WriteDataToDisk(const char *applicationDirectory);
void AddFile(const char *filename, const char *data, const unsigned dataLength, const unsigned fileLength, unsigned char context);
void AddFile(const char *filepath, const char *filename, unsigned char context);
void DeleteFiles(const char *applicationDirectory);
// Here so you can read it, but don't modify it
DataStructures::List<FileListNode> fileList;
protected:
};
#endif

408
raknet/FileListTransfer.cpp Normal file
View File

@ -0,0 +1,408 @@
#include "FileListTransfer.h"
#include "DS_HuffmanEncodingTree.h"
#include "FileListTransferCBInterface.h"
#include "StringCompressor.h"
#include "FileList.h"
#include "DS_Queue.h"
#include "PacketEnumerations.h"
#include "NetworkTypes.h"
#include "RakPeerInterface.h"
#include <assert.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
struct FileListTransfer::FileListReceiver
{
FileListTransferCBInterface *downloadHandler;
PlayerID allowedSender;
HuffmanEncodingTree tree;
unsigned short setID;
unsigned setCount;
unsigned setTotalCompressedTransmissionLength;
unsigned setTotalFinalLength;
bool gotSetHeader;
bool deleteDownloadHandler;
bool isCompressed;
};
FileListTransfer::FileListTransfer()
{
rakPeer=0;
setId=0;
DataStructures::Map<unsigned short, FileListReceiver*>::IMPLEMENT_DEFAULT_COMPARISON();
}
FileListTransfer::~FileListTransfer()
{
Clear();
}
unsigned short FileListTransfer::SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, PlayerID allowedSender)
{
if (rakPeer->GetIndexFromPlayerID(allowedSender)==-1)
return (unsigned short)-1;
FileListReceiver *receiver;
if (fileListReceivers.Has(setId))
{
receiver=fileListReceivers.Get(setId);
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, PlayerID 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 (compressData)
{
memset(frequencyTable,0,256*sizeof(unsigned int));
for (i=0; i < fileList->fileList.Size(); i++)
{
for (j=0; j < fileList->fileList[i].dataLength; j++)
{
++frequencyTable[(unsigned char)(fileList->fileList[i].data[j])];
}
}
tree.GenerateFromFrequencyTable(frequencyTable);
// Compress all the files, so we know the total compressed size to be sent
totalCompressedLength=totalLength=0;
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].fileLength==fileList->fileList[i].fileLength);
tree.EncodeArray((unsigned char*)fileList->fileList[i].data, fileList->fileList[i].dataLength, &encodedData);
n.dataLength=encodedData.GetNumberOfBitsUsed();
totalCompressedLength+=BITS_TO_BYTES(n.dataLength);
totalLength+=fileList->fileList[i].fileLength;
n.data = new char[BITS_TO_BYTES(n.dataLength)];
memcpy(n.data, encodedData.GetData(), BITS_TO_BYTES(n.dataLength));
compressedFiles.Push(n);
}
}
// Write the chunk header, which contains the frequency table, the total number of files, and the total number of bytes
bool anythingToWrite;
outBitstream.Write((unsigned char)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());
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);
// Send each possibly compressed file
for (i=0; i < compressedFiles.Size(); i++)
{
outBitstream.Reset();
outBitstream.Write((unsigned char)ID_FILE_LIST_TRANSFER_FILE);
outBitstream.Write(fileList->fileList[i].context);
outBitstream.Write(setID);
outBitstream.WriteCompressed(i);
outBitstream.WriteCompressed(fileList->fileList[i].dataLength); // Original length
if (compressData)
outBitstream.WriteCompressed(compressedFiles[i].dataLength); // Compressed bitlength }
stringCompressor->EncodeString(fileList->fileList[i].filename, 512, &outBitstream);
if (compressData)
{
outBitstream.WriteBits((const unsigned char*)compressedFiles[i].data, compressedFiles[i].dataLength);
delete [] compressedFiles[i].data;
}
else
outBitstream.WriteBits((const unsigned char*)fileList->fileList[i].data, fileList->fileList[i].dataLength);
rakPeer->Send(&outBitstream, 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;
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->playerId)
{
#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);
inBitStream.ReadCompressed(fileListReceiver->setTotalFinalLength);
fileListReceiver->setTotalCompressedTransmissionLength=fileListReceiver->setTotalFinalLength;
}
}
else
{
fileListReceiver->downloadHandler->OnFile(0, 0, 0, 0, 0, setID, 0, 0, 0, 0);
return true;
}
return false;
}
bool FileListTransfer::DecodeFile(Packet *packet, bool fullFile)
{
unsigned char *decompressedFileData;
char fileName[512];
unsigned char context;
unsigned fileIndex, fileLength, compressedLength;
unsigned bitLength;
unsigned len;
unsigned short setID;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
inBitStream.IgnoreBits(8);
unsigned int partCount;
unsigned int partTotal;
unsigned int partLength;
if (fullFile==false)
{
inBitStream.Read(partCount);
inBitStream.Read(partTotal);
inBitStream.Read(partLength);
inBitStream.IgnoreBits(8);
}
inBitStream.Read(context);
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->playerId)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
#ifdef _DEBUG
assert(fileListReceiver->gotSetHeader==true);
#endif
inBitStream.ReadCompressed(fileIndex);
inBitStream.ReadCompressed(fileLength);
if (fileListReceiver->isCompressed)
{
inBitStream.ReadCompressed(bitLength);
compressedLength=BITS_TO_BYTES(bitLength);
}
if (stringCompressor->DecodeString(fileName, 512, &inBitStream)==false)
{
#ifdef _DEBUG
assert(0);
#endif
return false;
}
if (fullFile)
{
decompressedFileData = new unsigned char [fileLength];
if (fileListReceiver->isCompressed)
{
len=fileListReceiver->tree.DecodeArray(&inBitStream, bitLength, fileLength, decompressedFileData);
if (len!=fileLength)
{
#ifdef _DEBUG
assert(0);
#endif
delete [] decompressedFileData;
return false;
}
}
else
inBitStream.Read((char*)decompressedFileData, fileLength);
}
// User callback for this file.
if (fullFile)
{
fileListReceiver->downloadHandler->OnFile(fileIndex, fileName, (char*)decompressedFileData, compressedLength, fileLength, setID, fileListReceiver->setCount, fileListReceiver->setTotalCompressedTransmissionLength, fileListReceiver->setTotalFinalLength, context);
delete [] decompressedFileData;
// If this set is done, free the memory for it.
if (fileListReceiver->setCount==fileIndex+1)
{
if (fileListReceiver->deleteDownloadHandler)
delete fileListReceiver->downloadHandler;
delete fileListReceiver;
fileListReceivers.Delete(setID);
}
}
else
fileListReceiver->downloadHandler->OnFileProgress(fileIndex, fileName, compressedLength, fileLength, setID, fileListReceiver->setCount, fileListReceiver->setTotalCompressedTransmissionLength, fileListReceiver->setTotalFinalLength, context, partCount, partTotal, partLength);
return true;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
PluginReceiveResult FileListTransfer::OnReceive(RakPeerInterface *peer, Packet *packet)
{
switch (packet->data[0])
{
case ID_CONNECTION_LOST:
case ID_DISCONNECTION_NOTIFICATION:
RemoveReceiver(packet->playerId);
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;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void FileListTransfer::OnDisconnect(RakPeerInterface *peer)
{
Clear();
}
void FileListTransfer::Clear(void)
{
unsigned i;
for (i=0; i < fileListReceivers.Size(); i++)
{
if (fileListReceivers[i]->deleteDownloadHandler)
delete fileListReceivers[i]->downloadHandler;
delete fileListReceivers[i];
}
fileListReceivers.Clear();
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void FileListTransfer::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId)
{
RemoveReceiver(playerId);
}
void FileListTransfer::RemoveReceiver(PlayerID playerId)
{
unsigned i;
i=0;
while (i < fileListReceivers.Size())
{
if (fileListReceivers[i]->allowedSender==playerId)
{
if (fileListReceivers[i]->deleteDownloadHandler)
delete fileListReceivers[i]->downloadHandler;
delete fileListReceivers[i];
fileListReceivers.RemoveAtIndex(i);
}
else
i++;
}
}
void FileListTransfer::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

89
raknet/FileListTransfer.h Normal file
View File

@ -0,0 +1,89 @@
/// \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.rakkarsoft.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 "NetworkTypes.h"
#include "Export.h"
#include "PluginInterface.h"
#include "DS_Map.h"
#include "NetworkTypes.h"
#include "PacketPriority.h"
class FileListTransferCBInterface;
class FileList;
/// \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 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();
~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, PlayerID 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, PlayerID recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData);
void RemoveReceiver(PlayerID playerId);
/// \internal For plugin handling
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// \internal For plugin handling
virtual void OnDisconnect(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId);
/// \internal For plugin handling
virtual void OnAttach(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;
};
#endif

View File

@ -0,0 +1,38 @@
#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
/// \brief Used by FileListTransfer plugin as a callback for when we get a file.
/// \sa FileListTransfer
class FileListTransferCBInterface
{
public:
// Got a file
virtual void OnFile(
unsigned fileIndex,
char *filename,
char *fileData,
unsigned compressedTransmissionLength,
unsigned finalDataLength,
unsigned short setID,
unsigned setCount,
unsigned setTotalCompressedTransmissionLength,
unsigned setTotalFinalLength,
unsigned char context)=0;
// Got part of a big file. Activate with RakPeer::SetSplitMessageProgressInterval
virtual void OnFileProgress(unsigned fileIndex,
char *filename,
unsigned compressedTransmissionLength,
unsigned finalDataLength,
unsigned short setID,
unsigned setCount,
unsigned setTotalCompressedTransmissionLength,
unsigned setTotalFinalLength,
unsigned char context,
unsigned int partCount,
unsigned int partTotal,
unsigned int partLength) {}
};
#endif

147
raknet/FileOperations.cpp Normal file
View File

@ -0,0 +1,147 @@
#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 "LinuxStrings.h"
#include "_findfirst.h"
#endif
bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength )
{
int index;
FILE *fp;
char *pathCopy;
#ifndef _WIN32
char *systemCommand;
#endif
if ( path == 0 || path[ 0 ] == 0 )
return false;
#ifndef _WIN32
systemCommand = new char [ strlen( path ) + 1 + 6 ];
#endif
pathCopy = new char [ strlen( path ) + 1 ];
strcpy( pathCopy, path );
index = 0;
while ( pathCopy[ index ] )
{
if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\')
{
pathCopy[ index ] = 0;
#ifdef _WIN32
_mkdir( pathCopy );
#else
mkdir( pathCopy, 0744 );
#endif
pathCopy[ index ] = '/';
}
index++;
}
if (data && dataLength)
{
fp = fopen( path, "wb" );
if ( fp == 0 )
{
delete [] pathCopy;
#ifndef _WIN32
delete [] systemCommand;
#endif
return false;
}
fwrite( data, 1, dataLength, fp );
fclose( fp );
}
else
{
#ifdef _WIN32
_mkdir( pathCopy );
#else
mkdir( pathCopy, 0744 );
#endif
}
delete [] pathCopy;
#ifndef _WIN32
delete [] systemCommand;
#endif
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;
}
}

12
raknet/FileOperations.h Normal file
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,98 @@
/// \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.rakkarsoft.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 "PacketEnumerations.h"
#include "BitStream.h"
#include "ConnectionGraph.h"
#include <string.h>
#include <assert.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
FullyConnectedMesh::FullyConnectedMesh()
{
pw=0;
}
FullyConnectedMesh::~FullyConnectedMesh()
{
if (pw)
delete [] pw;
}
void FullyConnectedMesh::Initialize(const char *password)
{
if (pw)
delete [] pw;
if (password && password[0])
{
pw = new char [strlen(password)+1];
strcpy(pw, password);
}
else
pw=0;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void FullyConnectedMesh::OnDisconnect(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void FullyConnectedMesh::Update(RakPeerInterface *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;
PlayerID node1, node2;
b.Read(node1);
b.Read(group1);
if (peer->GetIndexFromPlayerID(node1)==-1)
peer->Connect(peer->PlayerIDToDottedIP(node1), node1.port, pw, pw ? (int)strlen(pw) : 0);
b.Read(node2);
b.Read(group2);
if (peer->GetIndexFromPlayerID(node2)==-1)
peer->Connect(peer->PlayerIDToDottedIP(node2), node2.port, pw, pw ? (int)strlen(pw) : 0);
break;
}
}
return RR_CONTINUE_PROCESSING;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@ -0,0 +1,54 @@
/// \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.rakkarsoft.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;
#include "PluginInterface.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 Initialize(const char *password);
// --------------------------------------------------------------------------------------------
// Packet handling functions
// --------------------------------------------------------------------------------------------
virtual void OnDisconnect(RakPeerInterface *peer);
virtual void Update(RakPeerInterface *peer);
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
protected:
char *pw;
};
#endif

81
raknet/InternalPacket.h Normal file
View File

@ -0,0 +1,81 @@
/// \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.rakkarsoft.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 "NetworkTypes.h"
/// 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 unsigned short OrderingIndexType;
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 short MessageNumberType;
/// Holds a user message, and related information
struct InternalPacket
{
///True if this is an acknowledgment packet
//bool isAcknowledgement;
///A unique numerical identifier given to this user message
MessageNumberType messageNumber;
/// 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;
///How many bits the data is
unsigned int dataBitLength;
///Buffer is a pointer to the actual data, assuming this packet has data at all
unsigned char *data;
/// For checking packetloss at a particular send rate
unsigned histogramMarker;
};
#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.rakkarsoft.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 "InternalPacketPool.h"
#include <assert.h>
InternalPacketPool::InternalPacketPool()
{
// Speed things up by not reallocating at runtime when a mutex is locked.
pool.ClearAndForceAllocation( 64 );
unsigned i;
for (i=0; i < 64; i++)
pool.Push(new InternalPacket);
}
InternalPacketPool::~InternalPacketPool()
{
ClearPool();
}
void InternalPacketPool::ClearPool( void )
{
while ( pool.Size() )
delete pool.Pop();
}
/*
InternalPacket* InternalPacketPool::GetPointer( void )
{
if ( pool.Size() )
return pool.Pop();
return new InternalPacket;
}
*/
void InternalPacketPool::ReleasePointer( InternalPacket *p )
{
if ( p == 0 )
{
// Releasing a null pointer?
#ifdef _DEBUG
assert( 0 );
#endif
return ;
}
#ifdef _DEBUG
p->data=0;
#endif
//poolMutex.Lock();
pool.Push( p );
//poolMutex.Unlock();
}

View File

@ -0,0 +1,55 @@
/// \file
/// \brief \b [Internal] Memory pool for InternalPacket*
///
/// 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.rakkarsoft.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_POOL
#define __INTERNAL_PACKET_POOL
#include "DS_Queue.h"
#include "InternalPacket.h"
/// Handles of a pool of InternalPacket pointers. This is only here for efficiency.
/// \sa InternalPacket.h
class InternalPacketPool
{
public:
/// Constructor
InternalPacketPool();
/// Destructor
~InternalPacketPool();
/// Get an InternalPacket pointer. Will either allocate a new one or return one from the pool
/// \return An InternalPacket pointer.
InternalPacket* GetPointer( void )
{if ( pool.Size() )
return pool.Pop();
return new InternalPacket;}
/// Return an InternalPacket pointer to the pool.
/// \param[in] p A pointer to an InternalPacket you no longer need.
void ReleasePointer( InternalPacket *p );
// Delete all InternalPacket pointers in the pool.
void ClearPool( void );
private:
/// Queue of internal packets
DataStructures::Queue<InternalPacket*> pool;
};
#endif

View File

@ -0,0 +1,131 @@
#include "LightweightDatabaseClient.h"
#include "StringCompressor.h"
#include "PacketEnumerations.h"
#include "RakPeerInterface.h"
#include "TableSerializer.h"
#include "BitStream.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 **columnSubset, unsigned char numColumnSubset, DatabaseFilter *filter, unsigned char numFilters, unsigned *rowIds, unsigned char numRowIDs, PlayerID playerId, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
RakNet::BitStream out;
out.Write((unsigned char) 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++)
out.Write(columnSubset[i]);
out.Write(numFilters);
for (i=0; i < numFilters; i++)
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,playerId, broadcast);
}
void LightweightDatabaseClient::RemoveRow(const char *tableName, const char *removePassword, unsigned rowId, PlayerID playerId, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
RakNet::BitStream out;
out.Write((unsigned char) 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,playerId, broadcast);
}
void LightweightDatabaseClient::UpdateRow(const char *tableName, const char *updatePassword, RowUpdateMode updateMode, bool hasRowId, unsigned rowId, DatabaseCellUpdate *cellUpdates, unsigned char numCellUpdates, PlayerID playerId, bool broadcast)
{
if (tableName==0 || tableName[0]==0)
return;
if (rakPeer==0)
return;
if (cellUpdates==0 || numCellUpdates==0)
return;
RakNet::BitStream out;
out.Write((unsigned char) 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,playerId, broadcast);
}
PluginReceiveResult LightweightDatabaseClient::OnReceive(RakPeerInterface *peer, Packet *packet)
{
// All these messages are just returned to the user.
// switch (packet->data[0])
// {
// case ID_DATABASE_QUERY_REPLY:
// case ID_DATABASE_UNKNOWN_TABLE:
// case ID_DATABASE_INCORRECT_PASSWORD:
// }
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.rakkarsoft.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
~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] playerId 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 **columnSubset, unsigned char numColumnSubset, DatabaseFilter *filter, unsigned char numFilters, unsigned *rowIds, unsigned char numRowIDs, PlayerID playerId, 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. Required to be true for updating an existing row. 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] playerId 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, PlayerID playerId, 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] playerId 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, PlayerID playerId, 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,49 @@
#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
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,147 @@
/// \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.rakkarsoft.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
~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] oneRowPerSystemId 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,
char queryPassword[_SIMPLE_DATABASE_PASSWORD_LENGTH],
char updatePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH],
char removePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH],
bool oneRowPerSystemId,
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 OnDisconnect(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId);
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 oneRowPerSystemId;
bool onlyUpdateOwnRows;
bool removeRowOnPingFailure;
bool removeRowOnDisconnect;
bool autogenerateRowIDs;
char tableName[_SIMPLE_DATABASE_TABLE_NAME_LENGTH];
// Used if autogenerateRowIDs==true
unsigned nextRowId;
unsigned systemIdColumnIndex;
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, PlayerID playerId, unsigned *rowId);
bool RowHasIP(DataStructures::Table::Row *row, PlayerID playerId, unsigned systemIdColumnIndex);
DataStructures::Table::Row * AddRow(LightweightDatabaseServer::DatabaseTable *databaseTable, PlayerID playerId, bool hasRowId, unsigned rowId);
void RemoveRowsFromIP(PlayerID playerId);
};
#endif

19
raknet/LinuxStrings.cpp Normal file
View File

@ -0,0 +1,19 @@
#if (defined(__GNUC__) || defined(__GCCXML__))
#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);
}
//#ifndef __CYGWIN__
//int _unlink(const char* filename)
//{
// return remove(filename);
//}
//#endif
#endif

12
raknet/LinuxStrings.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _GCC_WIN_STRINGS
#define _GCC_WIN_STRINGS
#if (defined(__GNUC__) || defined(__GCCXML__))
int _stricmp(const char* s1, const char* s2);
int _strnicmp(const char* s1, const char* s2, size_t n);
//#ifndef __CYGWIN__
//int _unlink(const char* filename);
//#endif
#endif
#endif

276
raknet/LogCommandParser.cpp Normal file
View File

@ -0,0 +1,276 @@
#include "LogCommandParser.h"
#include "TransportInterface.h"
#include <memory.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#if (defined(__GNUC__) || defined(__GCCXML__))
#define _vsnprintf vsnprintf
#define _stricmp strcasecmp
#endif
#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()
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, PlayerID playerId, const char *originalString)
{
if (strcmp(command, "Subscribe")==0)
{
unsigned channelIndex;
if (numParameters==0)
{
Subscribe(playerId, 0);
transport->Send(playerId, "Subscribed to all channels.\r\n");
}
else if (numParameters==1)
{
if ((channelIndex=Subscribe(playerId, parameterList[0]))!=(unsigned)-1)
{
transport->Send(playerId, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]);
}
else
{
transport->Send(playerId, "Cannot find channel %s.\r\n", parameterList[0]);
PrintChannels(playerId, transport);
}
}
else
{
transport->Send(playerId, "Subscribe takes either 0 or 1 parameters.\r\n");
}
}
else if (strcmp(command, "Unsubscribe")==0)
{
unsigned channelIndex;
if (numParameters==0)
{
Unsubscribe(playerId, 0);
transport->Send(playerId, "Unsubscribed from all channels.\r\n");
}
else if (numParameters==1)
{
if ((channelIndex=Unsubscribe(playerId, parameterList[0]))!=(unsigned)-1)
{
transport->Send(playerId, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]);
}
else
{
transport->Send(playerId, "Cannot find channel %s.\r\n", parameterList[0]);
PrintChannels(playerId, transport);
}
}
else
{
transport->Send(playerId, "Unsubscribe takes either 0 or 1 parameters.\r\n");
}
}
return true;
}
char *LogCommandParser::GetName(void) const
{
return "Logger";
}
void LogCommandParser::SendHelp(TransportInterface *transport, PlayerID playerId)
{
transport->Send(playerId, "The logger will accept user log data via the Log(...) function.\r\n");
transport->Send(playerId, "Each log is associated with a named channel.\r\n");
transport->Send(playerId, "You can subscribe to or unsubscribe from named channels.\r\n");
PrintChannels(playerId, 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].playerId, text);
}
}
}
void LogCommandParser::PrintChannels(PlayerID playerId, TransportInterface *transport) const
{
unsigned i;
bool anyChannels=false;
transport->Send(playerId, "CHANNELS:\r\n");
for (i=0; i < 32; i++)
{
if (channelNames[i])
{
transport->Send(playerId, "%i. %s\r\n", i+1,channelNames[i]);
anyChannels=true;
}
}
if (anyChannels==false)
transport->Send(playerId, "None.\r\n");
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void LogCommandParser::OnNewIncomingConnection(PlayerID playerId, TransportInterface *transport)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void LogCommandParser::OnConnectionLost(PlayerID playerId, TransportInterface *transport)
{
Unsubscribe(playerId, 0);
}
unsigned LogCommandParser::Unsubscribe(PlayerID playerId, const char *channelName)
{
unsigned i;
for (i=0; i < remoteUsers.Size(); i++)
{
if (remoteUsers[i].playerId==playerId)
{
if (channelName==0)
{
// Unsubscribe from all and delete this user.
remoteUsers[i]=remoteUsers[remoteUsers.Size()-1];
remoteUsers.Del();
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(PlayerID playerId, 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].playerId==playerId)
{
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
PlayerIDAndChannel newUser;
newUser.playerId = playerId;
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

117
raknet/LogCommandParser.h Normal file
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.rakkarsoft.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] playerId 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, PlayerID playerId, 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.
char *GetName(void) const;
/// A callback for when you are expected to send a brief description of your parser to \a playerId
/// \param[in] transport The transport interface we can use to write to
/// \param[in] playerId The player that requested help.
void SendHelp(TransportInterface *transport, PlayerID playerId);
/// 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 playerId has connected to us.
/// \param[in] playerId 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(PlayerID playerId, TransportInterface *transport);
/// A callback for when \a playerId has disconnected, either gracefully or forcefully
/// \param[in] playerId The player that has disconnected.
/// \param[in] transport The transport interface that sent us this information.
void OnConnectionLost(PlayerID playerId, 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] playerId The player to send to
/// \param[in] transport The transport interface to use to send the channels
void PrintChannels(PlayerID playerId, TransportInterface *transport) const;
/// Unsubscribe a user from a channel (or from all channels)
/// \param[in] playerId The player to unsubscribe to
/// \param[in] channelName If 0, then unsubscribe from all channels. Otherwise unsubscribe from the named channel
unsigned Unsubscribe(PlayerID playerId, const char *channelName);
/// Subscribe a user to a channel (or to all channels)
/// \param[in] playerId The player to subscribe to
/// \param[in] channelName If 0, then subscribe from all channels. Otherwise subscribe to the named channel
unsigned Subscribe(PlayerID playerId, 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 PlayerIDAndChannel
{
/// The ID of the player
PlayerID playerId;
/// 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<PlayerIDAndChannel> 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

View File

@ -33,16 +33,12 @@
#ifdef _COMPATIBILITY_1
#define DEFAULT_MTU_SIZE 1264
#else
#define DEFAULT_MTU_SIZE 576
#define DEFAULT_MTU_SIZE 1500
#endif
/// The largest value for an UDP datagram
/// \sa RakPeer::SetMTUSize()
#ifdef SAMPCLI
#define MAXIMUM_MTU_SIZE 1492
#else
#define MAXIMUM_MTU_SIZE 576
#endif
#define MAXIMUM_MTU_SIZE 1500
#endif

19
raknet/Makefile Normal file
View File

@ -0,0 +1,19 @@
include $(BASE_DIR)/makefile.defs
all: shared static
shared:
mkdir -p $(BASE_DIR)/Lib/linux/
$(CC) $(DEBUG) -I$(INCLUDE) -w -c *.cpp
$(CC) $(DEBUG) -shared -Wl-soname,libraknet.so.2 -o $(BASE_DIR)/Lib/linux/libraknet.so.$(VERSION) *.o $(LIBS)
static:
mkdir -p $(BASE_DIR)/Lib/linux/
$(CC) $(DEBUG) -I$(INCLUDE) -w -c *.cpp
$(AR) rcs $(BASE_DIR)/Lib/linux/libraknet.a *.o
clean:
rm -f core
rm -f *.o
rm -f $(BASE_DIR)/Lib/linux/libraknet.so.$(VERSION)
rm -f $(BASE_DIR)/Lib/linux/linuxraknet.a

106
raknet/Makefile.am Normal file
View File

@ -0,0 +1,106 @@
# Makefile.am
# RakNet configure script
SUBDIRS=Autopatcher RakVoice .
lib_LTLIBRARIES=libRakNet.la
#RakNet headers
libRakNet_ladir=$(includedir)/RakNet
libRakNet_la_HEADERS=\
AES128.h\
ArrayList.h\
AsynchronousFileIO.h\
BigTypes.h\
BinarySearchTree.h\
BitStream.h\
CheckSum.h\
ClientContextStruct.h\
DataBlockEncryptor.h\
DistributedNetworkObject.h\
DistributedNetworkObjectHeader.h\
DistributedNetworkObjectManager.h\
DistributedNetworkObjectStub.h\
EncodeClassName.h\
ExtendedOverlappedPool.h\
GetTime.h\
HuffmanEncodingTree.h\
HuffmanEncodingTreeFactory.h\
HuffmanEncodingTreeNode.h\
InternalPacket.h\
InternalPacketPool.h\
LinkedList.h\
MTUSize.h\
Multiplayer.h\
NetworkObject.h\
NetworkTypes.h\
PacketEnumerations.h\
PacketPool.h\
PacketPriority.h\
Queue.h\
QueueLinkedList.h\
RPCNode.h\
RSACrypt.h\
RakClient.h\
RakClientInterface.h\
RakNetStatistics.h\
RakNetworkFactory.h\
RakPeer.h\
RakPeerInterface.h\
RakServer.h\
RakServerInterface.h\
Rand.h\
ReliabilityLayer.h\
SHA1.h\
SimpleMutex.h\
SocketLayer.h\
StringCompressor.h\
Types.h
#RakNet sources files
libRakNet_la_SOURCES=\
AES128.cpp\
AsynchronousFileIO.cpp\
BitStream.cpp\
CheckSum.cpp\
DataBlockEncryptor.cpp\
DistributedNetworkObject.cpp\
DistributedNetworkObjectManager.cpp\
DistributedNetworkObjectStub.cpp\
EncodeClassName.cpp\
ExtendedOverlappedPool.cpp\
GetTime.cpp\
HuffmanEncodingTree.cpp\
HuffmanEncodingTreeFactory.cpp\
InternalPacketPool.cpp\
NetworkObject.cpp\
NetworkTypes.cpp\
PacketPool.cpp\
RPCNode.cpp\
RakClient.cpp\
RakNetStatistics.cpp\
RakNetworkFactory.cpp\
RakPeer.cpp\
RakServer.cpp\
Rand.cpp\
ReliabilityLayer.cpp\
SHA1.cpp\
SimpleMutex.cpp\
SocketLayer.cpp\
StringCompressor.cpp
if BUILD_RAKVOICE
if BUILD_MONOLITHIC
libRakNet_la_LIBADD=\
Autopatcher/libRakNet_autopatcher.la\
RakVoice/libRakNet_voice.la
else
libRakNet_la_LIBADD=\
Autopatcher/libRakNet_autopatcher.la
endif
else
libRakNet_la_LIBADD=\
Autopatcher/libRakNet_autopatcher.la
endif

381
raknet/NatPunchthrough.cpp Normal file
View File

@ -0,0 +1,381 @@
#include "NatPunchthrough.h"
#include "GetTime.h"
#include "RakPeerInterface.h"
#include "BitStream.h"
#include "PacketEnumerations.h"
#include "RakAssert.h"
//#include <stdio.h>
// Number is arbitrary, but you want a long enough interval to get a good sample, but short enough not to annoy the user.
static const int PING_INTERVAL=100;
// Fill up the ping buffer
static const int PING_COUNT=PING_TIMES_ARRAY_SIZE;
// What multiple to multiply the ping by to figure out how long to wait before sending the start time.
// Should be slightly larger than the ping so the last ping arrives
static const float SEND_TIMESTAMP_DELAY_PING_MULTIPLE=1.5f;
// What multiple to multiply the ping by to figure out when to send the NAT punchthrough request
// Should be large enough to make sure both systems get the message in the future, even if it needs to be resent due to packetloss.
// Too large is bad because the user has to wait and it makes it more likely our estimated ping is wrong
static const float SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE=5.0f;
#ifdef _MSC_VER
#pragma warning( push )
#endif
NatPunchthrough::NatPunchthrough()
{
allowFacilitation=true;
rakPeer=0;
}
NatPunchthrough::~NatPunchthrough()
{
Clear();
}
void NatPunchthrough::FacilitateConnections(bool allow)
{
allowFacilitation=allow;
}
bool NatPunchthrough::Connect(const char* host, unsigned short remotePort, char* passwordData, int passwordDataLength, PlayerID facilitator)
{
PlayerID playerId;
rakPeer->IPToPlayerID(host, remotePort, &playerId);
return Connect(playerId, passwordData, passwordDataLength, facilitator);
}
bool NatPunchthrough::Connect(PlayerID receiver, char* passwordData, int passwordDataLength, PlayerID facilitator)
{
if (rakPeer->GetIndexFromPlayerID(facilitator)==-1)
return false;
RakNet::BitStream outBitstream;
outBitstream.Write((unsigned char) ID_NAT_PUNCHTHROUGH_REQUEST);
outBitstream.Write(receiver);
// Remember this connection request
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
connectionRequest->receiver=receiver;
connectionRequest->facilitator=facilitator;
if (passwordDataLength)
{
connectionRequest->passwordData = new char [passwordDataLength];
memcpy(connectionRequest->passwordData, passwordData, passwordDataLength);
}
else
connectionRequest->passwordData = 0;
connectionRequest->sender=UNASSIGNED_PLAYER_ID;
connectionRequest->passwordDataLength=passwordDataLength;
connectionRequest->facilitator=facilitator;
connectionRequest->nextActionTime=0;
connectionRequest->facilitatingConnection=false;
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
connectionRequest->pingCount=0;
connectionRequestList.Insert(connectionRequest);
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, facilitator, false);
return true;
}
void NatPunchthrough::Clear(void)
{
unsigned i;
for (i=0; i < connectionRequestList.Size(); i++)
{
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
}
connectionRequestList.Clear();
}
void NatPunchthrough::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::Update(RakPeerInterface *peer)
{
if (connectionRequestList.Size())
{
RakNetTime time = RakNet::GetTime();
RakNet::BitStream outBitstream;
unsigned i;
i=0;
while (i < connectionRequestList.Size())
{
// Remove old connection requests that get no answer
if (connectionRequestList[i]->timeoutTime < time)
{
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
connectionRequestList.RemoveAtIndex(i);
continue;
}
// If we are a facilitator, do pings
if (connectionRequestList[i]->facilitatingConnection)
{
if (time >= connectionRequestList[i]->nextActionTime )
{
if (connectionRequestList[i]->pingCount < PING_COUNT)
{
// Ping
connectionRequestList[i]->pingCount++;
rakPeer->Ping(connectionRequestList[i]->receiver);
rakPeer->Ping(connectionRequestList[i]->sender);
connectionRequestList[i]->nextActionTime=time+PING_INTERVAL;
}
else if (connectionRequestList[i]->pingCount == PING_COUNT)
{
// Done pinging. Wait till the next stage
int receiverPing, senderPing;
receiverPing=rakPeer->GetAveragePing(connectionRequestList[i]->receiver);
senderPing=rakPeer->GetAveragePing(connectionRequestList[i]->sender);
if (receiverPing > senderPing)
connectionRequestList[i]->nextActionTime=(RakNetTime)(receiverPing*SEND_TIMESTAMP_DELAY_PING_MULTIPLE);
else
connectionRequestList[i]->nextActionTime=(RakNetTime)(senderPing*SEND_TIMESTAMP_DELAY_PING_MULTIPLE);
connectionRequestList[i]->pingCount++;
}
else
{
// Send the timestamped message to both systems so they send datagrams to each other simultaneously
int receiverPing, senderPing;
RakNetTime delayTime;
receiverPing=rakPeer->GetAveragePing(connectionRequestList[i]->receiver);
senderPing=rakPeer->GetAveragePing(connectionRequestList[i]->sender);
if (receiverPing > senderPing)
delayTime=(RakNetTime)(receiverPing*SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE);
else
delayTime=(RakNetTime)(senderPing*SEND_PUNCHTHROUGH_DELAY_PING_MULTIPLE);
outBitstream.Reset();
outBitstream.Write((MessageID)ID_TIMESTAMP);
outBitstream.Write(time+delayTime);
outBitstream.Write((MessageID)ID_NAT_CONNECT_AT_TIME);
rakPeer->Send(&outBitstream, SYSTEM_PRIORITY, RELIABLE, 0, connectionRequestList[i]->sender, false);
outBitstream.Reset();
outBitstream.Write((MessageID)ID_TIMESTAMP);
outBitstream.Write(time+delayTime);
outBitstream.Write((MessageID)ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME);
outBitstream.Write(connectionRequestList[i]->sender);
rakPeer->Send(&outBitstream, SYSTEM_PRIORITY, RELIABLE, 0, connectionRequestList[i]->receiver, false);
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
connectionRequestList.RemoveAtIndex(i);
continue;
}
}
}
// Else not a facilitator. If nextActionTime is non-zero, connect or send the offline message
else if (connectionRequestList[i]->nextActionTime && time >= connectionRequestList[i]->nextActionTime)
{
if (connectionRequestList[i]->receiver!=UNASSIGNED_PLAYER_ID)
{
// Connect to this system.
rakPeer->Connect(rakPeer->PlayerIDToDottedIP(connectionRequestList[i]->receiver), connectionRequestList[i]->receiver.port, connectionRequestList[i]->passwordData, connectionRequestList[i]->passwordDataLength);
}
else
{
RakAssert(connectionRequestList[i]->sender!=UNASSIGNED_PLAYER_ID);
// Send offline message to this system, hopefully at the exact same time that system tries to connect to us.
rakPeer->Ping(rakPeer->PlayerIDToDottedIP(connectionRequestList[i]->sender), connectionRequestList[i]->sender.port, false);
}
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
connectionRequestList.RemoveAtIndex(i);
continue;
}
i++;
}
}
}
PluginReceiveResult NatPunchthrough::OnReceive(RakPeerInterface *peer, Packet *packet)
{
unsigned char packetId;
if (packet->length>sizeof(MessageID)+sizeof(RakNetTime) && packet->data[0]==ID_TIMESTAMP)
packetId=packet->data[sizeof(MessageID)+sizeof(RakNetTime)];
else
packetId=packet->data[0];
switch (packetId)
{
case ID_NAT_PUNCHTHROUGH_REQUEST:
OnPunchthroughRequest(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
case ID_NAT_TARGET_CONNECTION_LOST:
case ID_NAT_TARGET_NOT_CONNECTED:
// Sender only
return RemoveRequestByFacilitator(packet->playerId);
case ID_NAT_CONNECT_AT_TIME:
OnConnectAtTime(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
case ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME:
OnSendOfflineMessageAtTime(peer, packet);
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Absorb
case ID_DISCONNECTION_NOTIFICATION:
case ID_CONNECTION_LOST:
OnCloseConnection(peer, packet->playerId);
return RR_CONTINUE_PROCESSING;
}
return RR_CONTINUE_PROCESSING;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId)
{
if (allowFacilitation==false)
return;
// If in ping mode, send ID_NAT_TARGET_CONNECTION_LOST
if (connectionRequestList.Size())
{
unsigned i;
i=0;
while (i < connectionRequestList.Size())
{
if (connectionRequestList[i]->facilitatingConnection &&
(connectionRequestList[i]->receiver==playerId ||
connectionRequestList[i]->sender==playerId))
{
// This field is not used by the facilitator.
RakAssert(connectionRequestList[i]->passwordData==0);
if (connectionRequestList[i]->sender==playerId)
{
RakNet::BitStream outBitstream;
outBitstream.Write((unsigned char)ID_NAT_TARGET_CONNECTION_LOST);
outBitstream.Write(connectionRequestList[i]->receiver);
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, connectionRequestList[i]->sender, false);
}
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
connectionRequestList.RemoveAtIndex(i);
}
else
i++;
}
}
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::OnDisconnect(RakPeerInterface *peer)
{
}
PluginReceiveResult NatPunchthrough::RemoveRequestByFacilitator(PlayerID playerId)
{
unsigned i;
i=0;
while (i < connectionRequestList.Size())
{
if (connectionRequestList[i]->facilitator==playerId)
{
delete [] connectionRequestList[i]->passwordData;
delete connectionRequestList[i];
connectionRequestList.RemoveAtIndex(i);
return RR_CONTINUE_PROCESSING;
}
else
i++;
}
return RR_STOP_PROCESSING_AND_DEALLOCATE;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::OnPunchthroughRequest(RakPeerInterface *peer, Packet *packet)
{
if (allowFacilitation==false)
return;
PlayerID target;
RakNet::BitStream inBitstream(packet->data, packet->length, false);
RakNet::BitStream outBitstream;
inBitstream.IgnoreBits(8);
if (inBitstream.Read(target)==false)
return;
if (rakPeer->GetIndexFromPlayerID(target)==-1)
{
outBitstream.Write((unsigned char)ID_NAT_TARGET_NOT_CONNECTED);
outBitstream.Write(target);
rakPeer->Send(&outBitstream, HIGH_PRIORITY, RELIABLE, 0, packet->playerId, false);
}
else
{
// Remember this connection request
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
connectionRequest->receiver=target;
connectionRequest->facilitator=UNASSIGNED_PLAYER_ID;
connectionRequest->passwordData = 0;
connectionRequest->sender=packet->playerId;
connectionRequest->passwordDataLength=0;
connectionRequest->facilitatingConnection=true;
connectionRequest->nextActionTime=RakNet::GetTime()+PING_INTERVAL;
connectionRequest->pingCount=1;
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
connectionRequestList.Insert(connectionRequest);
rakPeer->Ping(connectionRequest->receiver);
rakPeer->Ping(connectionRequest->sender);
}
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::OnConnectAtTime(RakPeerInterface *peer, Packet *packet)
{
// Find this connection request and set the connection timer.
unsigned i;
for (i=0; i < connectionRequestList.Size(); i++)
{
if (connectionRequestList[i]->nextActionTime==0 &&
connectionRequestList[i]->facilitatingConnection==false &&
connectionRequestList[i]->facilitator==packet->playerId)
{
RakNet::BitStream inBitstream(packet->data, packet->length, false);
inBitstream.IgnoreBits(8);
inBitstream.Read(connectionRequestList[i]->nextActionTime);
return;
}
}
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void NatPunchthrough::OnSendOfflineMessageAtTime(RakPeerInterface *peer, Packet *packet)
{
RakNet::BitStream inBitstream(packet->data, packet->length, false);
RakNetTime nextActionTime;
inBitstream.IgnoreBits(8);
PlayerID sender;
inBitstream.Read(nextActionTime);
inBitstream.IgnoreBits(8);
if (inBitstream.Read(sender)==false)
return;
NatPunchthrough::ConnectionRequest *connectionRequest = new NatPunchthrough::ConnectionRequest;
connectionRequest->receiver=UNASSIGNED_PLAYER_ID;
connectionRequest->facilitator=packet->playerId;
connectionRequest->sender=sender;
connectionRequest->passwordData = 0;
connectionRequest->passwordDataLength=0;
connectionRequest->facilitatingConnection=false;
connectionRequest->nextActionTime=nextActionTime;
connectionRequest->pingCount=0;
connectionRequest->timeoutTime=RakNet::GetTime()+30000;
connectionRequestList.Insert(connectionRequest);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

143
raknet/NatPunchthrough.h Normal file
View File

@ -0,0 +1,143 @@
/// \file
/// \brief Contains the NAT-punchthrough 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.rakkarsoft.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 __NAT_PUNCHTHROUGH_H
#define __NAT_PUNCHTHROUGH_H
#include "NetworkTypes.h"
#include "Export.h"
#include "PluginInterface.h"
#include "PacketPriority.h"
#include "DS_List.h"
class RakPeerInterface;
struct Packet;
/// \defgroup NAT_PUNCHTHROUGH_GROUP NatPunchthrough
/// \ingroup PLUGINS_GROUP
/// \brief The NatPunchthrough class implements the NAT punch through technique, allowing two systems to connect to each other that are both behind NATs.
///
/// A NAT (Network Address Translator) is a system that will makes it so your system's IP address is different from the IP address exposed to the internet.
/// This provides some security and allows multiple computers, each with a different IP address, to share one IP address as seen by the internet.
///
/// The problem is that NATs also ignore packets sent to them unless they sent a packet to the sender first.
/// If two systems are both behind NATs, then neither system can connect to each other.
/// Furthermore, some NATs will impose a temporary ban on an IP address that send unsolicited packets to them.
///
/// This can be solved by using a third system, a facilitator, that is not behind a NAT and that both systems are already connected to.
/// It will synchronize a send between both NAT systems such that the routers will both consider themselves as handling a reply to a message, when in
/// fact they are handing an initial message. As replies are allowed, both systems get their corresponding messages and the connection takes place.
/// S = system that wants to connect
/// F = facilitator
/// R = system to get the connection request.
///
/// S knows IP of R in advance.
/// 1. S->F facilitate connection request to R
/// 2. if (R is not is connected) F->S ID_NAT_TARGET_NOT_CONNECTED. Exit.
/// 3. F -> (Ping S, Ping R), every X ms Y times. Wait Max(Ping(s), Ping(r) * multiple ms more, then go to step 4.
/// 4. F picks time highest(ave of Ping R,S) * N from now and sends this time RELIABLE and timestamped to R,S.
/// 5. At time picked in (4), S attempts to connect to R. R sends offline ping to S.
/// 6. If R disconnects before or at step 4, tell this to S via ID_NAT_TARGET_CONNECTION_LOST
///
/// \note Timing is important with this plugin. You need to call RakPeer::Receive frequently.
/// \ingroup NAT_PUNCHTHROUGH_GROUP
class RAK_DLL_EXPORT NatPunchthrough : public PluginInterface
{
public:
/// Constructor
NatPunchthrough();
/// Destructor
~NatPunchthrough();
/// Call with true to allow other systems to use this system as a NAT punch through facilitator. This takes a little bandwidth but
/// otherwise there is no reason to disallow it.
/// Defaults to true
/// \param[in] allow True to allow, false to disallow.
void FacilitateConnections(bool allow);
/// Call this to start to connect to the specified host (ip or domain name) and server port using \a facilitator to punch through a NAT
/// This is a non-blocking operation
/// You know the connection is successful when you get the message ID_CONNECTION_ACCEPTED.
/// You know the connection failed when you get the message ID_CONNECTION_ATTEMPT_FAILED, ID_CONNECTION_BANNED, or ID_NAT_TARGET_NOT_CONNECTED
/// Both you and the host must be connected to the facilitator.
/// \pre Requires that you first call Initialize
/// \pre Both \a host and this system must already be connected to the system at the address \a facilitator and facilitator must be running NatPunchthrough with FacilitateConnections(true) previously called.
/// \param[in] receiver Either a dotted IP address or a domain name of the system you ultimately want to connect to.
/// \param[in] remotePort Which port to connect to of the system you ultimately want to connect to.
/// \param[in] passwordData A data block that must match the data block on the \a host. This can be just a password, or can be a stream of data
/// \param[in] passwordDataLength The length in bytes of passwordData
/// \return If you are not connected to the facilitator this function returns false. Otherwise it returns true.
bool Connect(const char* receiver, unsigned short remotePort, char* passwordData, int passwordDataLength, PlayerID facilitator);
/// Same as above, but takes a PlayerID for a host
/// \param[in] receiver The address of the host to connect to.
/// \param[in] remotePort Which port to connect to of the system you ultimately want to connect to.
/// \param[in] passwordData A data block that must match the data block on the \a host. This can be just a password, or can be a stream of data
/// \param[in] passwordDataLength The length in bytes of passwordData
/// \return If you are not connected to the facilitator this function returns false. Otherwise it returns true.
bool Connect(PlayerID receiver, char* passwordData, int passwordDataLength, PlayerID facilitator);
/// Free internal memory.
void Clear(void);
/// \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 OnDisconnect(RakPeerInterface *peer);
/// \internal For plugin handling
virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId);
struct ConnectionRequest
{
// Used by all
// 0 means unset. non-zero means send a connection request or offline message at that time.
// If the sender is set, then we send an offline message. If the reciever is set, we send a connection request.
RakNetTime nextActionTime;
// Used by sender and facilitator
bool facilitatingConnection;
PlayerID receiver;
// Used to remove old connection Requests
RakNetTime timeoutTime;
// Used only by sender
PlayerID facilitator;
char* passwordData;
int passwordDataLength;
// Used only by facilitator
PlayerID sender;
unsigned char pingCount;
};
protected:
void OnPunchthroughRequest(RakPeerInterface *peer, Packet *packet);
void OnConnectAtTime(RakPeerInterface *peer, Packet *packet);
void OnSendOfflineMessageAtTime(RakPeerInterface *peer, Packet *packet);
PluginReceiveResult RemoveRequestByFacilitator(PlayerID playerId);
bool allowFacilitation;
RakPeerInterface *rakPeer;
DataStructures::List<ConnectionRequest*> connectionRequestList;
};
#endif

View File

@ -0,0 +1,291 @@
/// \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.rakkarsoft.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 "NetworkIDGenerator.h"
#ifdef _COMPATIBILITY_1
#elif defined(_WIN32)
#include <malloc.h> // alloca
#elif defined(_COMPATIBILITY_2)
#include "Compatibility2Includes.h"
#else
#include <stdlib.h> // alloca
#endif
#include <assert.h>
// Note you will need to save and load this if your game supports saving and loading so you start at the same index you left off.
// If you don't do this you can overwrite indices
unsigned short NetworkIDGenerator::staticItemID = 0;
DataStructures::AVLBalancedBinarySearchTree<NetworkIDNode> NetworkIDGenerator::IDTree;
PlayerID NetworkIDGenerator::externalPlayerId=UNASSIGNED_PLAYER_ID;
bool NetworkIDNode::operator==( const NetworkIDNode& right ) const
{
if ( networkID == right.networkID )
return !0;
return 0;
}
bool NetworkIDNode::operator > ( const NetworkIDNode& right ) const
{
if ( networkID > right.networkID )
return !0;
return 0;
}
bool NetworkIDNode::operator < ( const NetworkIDNode& right ) const
{
if ( networkID < right.networkID )
return !0;
return 0;
}
NetworkIDNode::NetworkIDNode()
{
object = 0;
}
NetworkIDNode::NetworkIDNode( NetworkID _networkID, NetworkIDGenerator *_object )
{
networkID = _networkID;
object = _object;
}
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
NetworkIDGenerator::NetworkIDGenerator()
{
callGenerationCode=true;
networkID=UNASSIGNED_NETWORK_ID;
parent=0;
}
//-------------------------------------------------------------------------------------
NetworkIDGenerator::~NetworkIDGenerator()
{
if (networkID!=UNASSIGNED_NETWORK_ID)
{
NetworkIDNode * object = NetworkIDGenerator::IDTree.GetPointerToNode( NetworkIDNode( ( networkID ), 0 ) );
if ( object->object == this )
IDTree.Del( NetworkIDNode( object->networkID, 0 ) );
}
}
//////////////////////////////////////////////////////////////////////
// Public Methods
//////////////////////////////////////////////////////////////////////
NetworkID NetworkIDGenerator::GetNetworkID( void )
{
if (callGenerationCode && IsNetworkIDAuthority())
{
GenerateID();
callGenerationCode=false;
}
return networkID;
};
//-------------------------------------------------------------------------------------
unsigned short NetworkIDGenerator::GetStaticNetworkID( void )
{
return staticItemID;
}
//-------------------------------------------------------------------------------------
void NetworkIDGenerator::SetStaticNetworkID( unsigned short i )
{
staticItemID = i;
}
//-------------------------------------------------------------------------------------
void NetworkIDGenerator::SetExternalPlayerID(PlayerID playerId)
{
assert(playerId!=UNASSIGNED_PLAYER_ID);
externalPlayerId=playerId;
}
//-------------------------------------------------------------------------------------
PlayerID NetworkIDGenerator::GetExternalPlayerID(void)
{
return externalPlayerId;
}
//-------------------------------------------------------------------------------------
bool NetworkIDGenerator::RequiresSetParent(void) const
{
return false;
}
//-------------------------------------------------------------------------------------
void NetworkIDGenerator::SetNetworkID( NetworkID id )
{
callGenerationCode=false;
if ( id == UNASSIGNED_NETWORK_ID )
{
// puts("Warning: NetworkIDGenerator passed UNASSIGNED_NETWORK_ID. SetID ignored");
return ;
}
if ( networkID == id )
{
// printf("NetworkIDGenerator passed %i which already exists in the tree. SetID ignored", id);
return ;
}
NetworkIDNode* collision = NetworkIDGenerator::IDTree.GetPointerToNode( NetworkIDNode( ( id ), 0 ) );
if ( collision ) // Tree should have only unique values. The new value is already in use.
{
//printf("Warning: NetworkIDGenerator::SetID passed %i, which has an existing node in the tree. Old node removed, which will cause the item pointed to to be inaccessible to the network", id);
IDTree.Del( NetworkIDNode( collision->networkID, collision->object ) );
}
if ( networkID == UNASSIGNED_NETWORK_ID ) // Object has not had an ID assigned so does not already exist in the tree
{
networkID = id;
IDTree.Add( NetworkIDNode( networkID, this ) );
}
else // Object already exists in the tree and has an assigned ID
{
IDTree.Del( NetworkIDNode( networkID, this ) ); // Delete the node with whatever ID the existing object is using
networkID = id;
IDTree.Add( NetworkIDNode( networkID, this ) );
}
}
//-------------------------------------------------------------------------------------
void NetworkIDGenerator::SetParent( void *_parent )
{
parent=_parent;
#ifdef _DEBUG
// Avoid duplicate parents in the tree
unsigned size = IDTree.Size();
NetworkIDNode *nodeArray;
bool usedAlloca=false;
#if !defined(_COMPATIBILITY_1)
if (sizeof(NetworkIDNode) * size < MAX_ALLOCA_STACK_ALLOCATION)
{
nodeArray = (NetworkIDNode*) alloca(sizeof(NetworkIDNode) * size);
usedAlloca=true;
}
else
#endif
nodeArray = new NetworkIDNode[size];
IDTree.DisplayBreadthFirstSearch( nodeArray );
unsigned i;
for (i=0; i < size; i++)
{
// If this assert hits then this _parent is already in the tree. Classes instance should never contain more than one NetworkIDGenerator
assert(nodeArray->object->GetParent()!=parent);
}
if (usedAlloca==false)
delete [] nodeArray;
#endif
}
//-------------------------------------------------------------------------------------
void* NetworkIDGenerator::GetParent( void ) const
{
return parent;
}
//-------------------------------------------------------------------------------------
void NetworkIDGenerator::GenerateID(void)
{
assert(IsNetworkIDAuthority());
NetworkIDNode* collision;
do
{
networkID.localSystemId=staticItemID++;
if (NetworkID::peerToPeerMode)
{
// If this assert hits you forgot to call SetExternalPlayerID
assert(externalPlayerId!=UNASSIGNED_PLAYER_ID);
networkID.playerId=externalPlayerId;
}
collision = NetworkIDGenerator::IDTree.GetPointerToNode( NetworkIDNode( ( networkID ), 0 ) );
}
while ( collision );
IDTree.Add( NetworkIDNode( networkID, this ) );
}
//-------------------------------------------------------------------------------------
void* NetworkIDGenerator::GET_BASE_OBJECT_FROM_ID( NetworkID x )
{
if ( x == UNASSIGNED_NETWORK_ID )
return 0;
NetworkIDNode *n = NetworkIDGenerator::IDTree.GetPointerToNode( NetworkIDNode( ( x ), 0 ) );
if ( n )
{
return n->object;
}
return 0;
}
//-------------------------------------------------------------------------------------
void* NetworkIDGenerator::GET_OBJECT_FROM_ID( NetworkID x )
{
NetworkIDGenerator *object = (NetworkIDGenerator *) GET_BASE_OBJECT_FROM_ID( x );
if (object)
{
if (object->GetParent())
{
return object->GetParent();
}
else
{
#ifdef _DEBUG
// If this assert hit then this object requires a call to SetParent and it never got one.
assert(object->RequiresSetParent()==false);
#endif
return object;
}
}
return 0;
}
//-------------------------------------------------------------------------------------
void* GET_OBJECT_FROM_ID( NetworkID x )
{
return NetworkIDGenerator::GET_OBJECT_FROM_ID( x );
}
//////////////////////////////////////////////////////////////////////
// EOF
//////////////////////////////////////////////////////////////////////

134
raknet/NetworkIDGenerator.h Normal file
View File

@ -0,0 +1,134 @@
/// \file
/// \brief A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network.
///
/// 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.rakkarsoft.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(__NETWORK_ID_GENERATOR)
#define __NETWORK_ID_GENERATOR
#include "DS_BinarySearchTree.h"
#include "NetworkTypes.h"
#include "Export.h"
class NetworkIDGenerator;
/// \internal
/// \brief A node in the AVL tree that holds the mapping between NetworkID and pointers.
struct RAK_DLL_EXPORT NetworkIDNode
{
NetworkID networkID;
NetworkIDGenerator *object;
NetworkIDNode();
NetworkIDNode( NetworkID _networkID, NetworkIDGenerator *_object );
bool operator==( const NetworkIDNode& right ) const;
bool operator > ( const NetworkIDNode& right ) const;
bool operator < ( const NetworkIDNode& right ) const;
};
/// \brief Unique shared ids for each object instance
///
/// A class you can derive from to make it easier to represent every networked object with an integer. This way you can refer to objects over the network.
/// One system should return true for IsNetworkIDAuthority() and the rest should return false. When an object needs to be created, have the the one system create the object.
/// Then have that system send a message to all other systems, and include the value returned from GetNetworkID() in that packet. All other systems should then create the same
/// class of object, and call SetNetworkID() on that class with the NetworkID in the packet.
/// \see the manual for more information on this.
class RAK_DLL_EXPORT NetworkIDGenerator
{
public:
/// Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here.
NetworkIDGenerator();
/// Destructor. Used NetworkIDs, if any, are freed here.
virtual ~NetworkIDGenerator();
/// Returns the NetworkID that you can use to refer to this object over the network.
/// \retval UNASSIGNED_NETWORK_ID UNASSIGNED_NETWORK_ID is returned IsNetworkIDAuthority() is false and SetNetworkID() was not previously called. This is also returned if you call this function in the constructor.
/// \retval 0-65534 Any other value is a valid NetworkID. NetworkIDs start at 0 and go to 65534, wrapping at that point.
virtual NetworkID GetNetworkID( void );
/// Sets the NetworkID for this instance. Usually this is called by the clients and determined from the servers. However, if you save multiplayer games you would likely use
/// This on load as well.
virtual void SetNetworkID( NetworkID id );
/// Your class does not have to derive from NetworkIDGenerator, although that is the easiest way to implement this.
/// If you want this to be a member object of another class, rather than inherit, then call SetParent() with a pointer to the parent class instance.
/// GET_OBJECT_FROM_ID will then return the parent rather than this instance.
virtual void SetParent( void *_parent );
/// Return what was passed to SetParent
/// \return The value passed to SetParent, or 0 if it was never called.
virtual void* GetParent( void ) const;
/// This AVL tree holds the pointer to NetworkID mappings
static DataStructures::AVLBalancedBinarySearchTree<NetworkIDNode> IDTree;
/// These function is only meant to be used when saving games as you
/// should save the HIGHEST value staticItemID has achieved upon save
/// and reload it upon load. Save AFTER you've created all the items
/// derived from this class you are going to create.
/// \return the HIGHEST Object Id currently used
static unsigned short GetStaticNetworkID( void );
/// These function is only meant to be used when loading games. Load
/// BEFORE you create any new objects that are not SetIDed based on
/// the save data.
/// \param[in] i the highest number of NetworkIDGenerator reached.
static void SetStaticNetworkID( unsigned short i );
/// For every group of systems, one system needs to be responsible for creating unique IDs for all objects created on all systems.
/// This way, systems can send that id in packets to refer to objects (you can't send pointers because the memory allocations may be different).
/// In a client/server enviroment, the system that creates unique IDs would be the server.
/// return true if this system is responsible for creating unique IDs (probably the server), return true. Otherwise return false.
virtual bool IsNetworkIDAuthority(void) const=0;
/// Necessary for peer to peer, as NetworkIDs are then composed of your external player Id (doesn't matter which, as long as unique)
/// plus the usual object ID number.
/// Get this from RakPeer::GetExternalPlayerID) one time, the first time you make a connection.
/// \param[in] playerId Your external playerID
static void SetExternalPlayerID(PlayerID playerId);
static PlayerID GetExternalPlayerID(void);
/// Overload this function and return true if you require that SetParent is called before this object is used.
/// This is a safety check you should do this if you want this to be
/// a member object of another class rather than derive from this class.
virtual bool RequiresSetParent(void) const;
/// If you use a parent, returns this instance rather than the parent object.
static void* GET_BASE_OBJECT_FROM_ID( NetworkID x );
/// Returns the parent object, or this instance if you don't use a parent.
static void* GET_OBJECT_FROM_ID( NetworkID x );
protected:
/// The network ID of this object
NetworkID networkID;
/// The parent set by SetParent()
void *parent;
/// Internal function to generate an ID when needed. This is deferred until needed and is not called from the constructor.
void GenerateID(void);
/// This is crap but is necessary because virtual functions don't work in the constructor
bool callGenerationCode;
private:
static PlayerID externalPlayerId;
static unsigned short staticItemID;
};
#endif

View File

@ -1,8 +1,181 @@
// TODO: Implement NetworkTypes.cpp
/// \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.rakkarsoft.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 "NetworkTypes.h"
#include <string.h>
#include <stdio.h>
#ifdef _COMPATIBILITY_1
#include "Compatibility1Includes.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>
#include <stdlib.h> // itoa
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif
// 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* my_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;
}
// Defaults to not in peer to peer mode for NetworkIDs. This only sends the localSystemId portion in the BitStream class
// This is what you want for client/server, where the server assigns all NetworkIDs and it is unnecessary to transmit the full structure.
// For peer to peer, this will transmit the playerId of the system that created the object in addition to localSystemId. This allows
// Any system to create unique ids locally.
// All systems must use the same value for this variable.
bool RAK_DLL_EXPORT NetworkID::peerToPeerMode=false;
bool PlayerID::operator==( const PlayerID& right ) const
{
return binaryAddress == right.binaryAddress && port == right.port;
}
bool PlayerID::operator!=( const PlayerID& right ) const
{
return binaryAddress != right.binaryAddress || port != right.port;
}
bool PlayerID::operator>( const PlayerID& right ) const
{
return ( ( binaryAddress > right.binaryAddress ) || ( ( binaryAddress == right.binaryAddress ) && ( port > right.port ) ) );
}
bool PlayerID::operator<( const PlayerID& right ) const
{
return ( ( binaryAddress < right.binaryAddress ) || ( ( binaryAddress == right.binaryAddress ) && ( port < right.port ) ) );
}
char *PlayerID::ToString(bool writePort) const
{
#ifdef _COMPATIBILITY_1
return "";
#else
static char str[22];
in_addr in;
in.s_addr = binaryAddress;
strcpy(str, inet_ntoa( in ));
if (writePort)
{
strcat(str, ":");
#if (defined(__GNUC__) || defined(__GCCXML__))
my_itoa(port, str+strlen(str), 10);
#else
_itoa(port, str+strlen(str), 10);
#endif
}
return (char*) str;
#endif
}
void PlayerID::SetBinaryAddress(const char *str)
{
#ifdef _COMPATIBILITY_1
binaryAddress=UNASSIGNED_PLAYER_ID.binaryAddress;
#else
binaryAddress=inet_addr(str);
#endif
}
NetworkID& NetworkID::operator = ( const NetworkID& input )
{
playerId = input.playerId;
localSystemId = input.localSystemId;
return *this;
}
bool NetworkID::operator==( const NetworkID& right ) const
{
if (NetworkID::peerToPeerMode)
return playerId == right.playerId && localSystemId == right.localSystemId;
else
return localSystemId==right.localSystemId;
}
bool NetworkID::operator!=( const NetworkID& right ) const
{
if (NetworkID::peerToPeerMode)
return playerId != right.playerId || localSystemId != right.localSystemId;
else
return localSystemId!=right.localSystemId;
}
bool NetworkID::operator>( const NetworkID& right ) const
{
if (NetworkID::peerToPeerMode)
return ( ( playerId > right.playerId ) || ( ( playerId == right.playerId ) && ( localSystemId > right.localSystemId ) ) );
else
return localSystemId>right.localSystemId;
}
bool NetworkID::operator<( const NetworkID& right ) const
{
if (NetworkID::peerToPeerMode)
return ( ( playerId < right.playerId ) || ( ( playerId == right.playerId ) && ( localSystemId < right.localSystemId ) ) );
else
return localSystemId<right.localSystemId;
}
bool NetworkID::IsPeerToPeerMode(void)
{
return peerToPeerMode;
}
void NetworkID::SetPeerToPeerMode(bool isPeerToPeer)
{
peerToPeerMode=isPeerToPeer;
}

View File

@ -1,12 +1,45 @@
// TODO: Implement NetworkTypes.h
/// \file
/// \brief Types used by RakNet, most of which involve user code.
///
/// 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.rakkarsoft.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 __NETWORK_TYPES_H
#define __NETWORK_TYPES_H
#include "RakNetDefines.h"
#include "Export.h"
/// Forward declaration
namespace RakNet
{
class BitStream;
};
/// Given a number of bits, return how many bytes are needed to represent that.
#define BITS_TO_BYTES(x) (((x)+7)>>3)
#define BYTES_TO_BITS(x) ((x)<<3)
/// \sa NetworkIDGenerator.h
typedef unsigned char UniqueIDType;
typedef unsigned short PlayerIndex;
typedef unsigned char RPCIndex;
const int MAX_RPC_MAP_SIZE=((RPCIndex)-1)-1;
const int UNDEFINED_RPC_INDEX=((RPCIndex)-1);
/// First byte of a network message
typedef unsigned char MessageID;
// Define __GET_TIME_64BIT if you want to use large types for GetTime (takes more bandwidth when you transmit time though!)
// You would want to do this if your system is going to run long enough to overflow the millisecond counter (over a month)
@ -27,19 +60,67 @@ struct RAK_DLL_EXPORT PlayerID
///The port number
unsigned short port;
// Return the playerID as a string in the format <IP>:<Port>
// Note - returns a static string. Not thread-safe or safe for multiple calls per line.
char *ToString(bool writePort=true) const;
// Sets the binary address part from a string. Doesn't set the port
void SetBinaryAddress(const char *str);
PlayerID& operator = ( const PlayerID& input )
{
binaryAddress = input.binaryAddress;
port = input.port;
return *this;
}
bool operator==( const PlayerID& right ) const;
bool operator!=( const PlayerID& right ) const;
bool operator > ( const PlayerID& right ) const;
bool operator < ( const PlayerID& right ) const;
};
struct RAK_DLL_EXPORT NetworkID
{
// Set this to true to use peer to peer mode for NetworkIDs.
// Obviously the value of this must match on all systems.
// True, and this will write the playerId portion with network sends. Takes more bandwidth, but NetworkIDs can be locally generated
// False, and only localSystemId is used.
static bool peerToPeerMode;
// In peer to peer, we use both playerId and localSystemId
// In client / server, we only use localSystemId
PlayerID playerId;
unsigned short localSystemId;
NetworkID& operator = ( const NetworkID& input );
static bool IsPeerToPeerMode(void);
static void SetPeerToPeerMode(bool isPeerToPeer);
bool operator==( const NetworkID& right ) const;
bool operator!=( const NetworkID& right ) const;
bool operator > ( const NetworkID& right ) const;
bool operator < ( const NetworkID& right ) const;
};
/// Size of PlayerID data
#define PlayerID_Size 6
/// This represents a user message from another system.
struct Packet
{
char _gap0[8];
/// Server only - this is the index into the player array that this playerId maps to
PlayerIndex playerIndex;
/// The system that send this packet.
PlayerID playerId;
/// The length of the data in bytes
/// \deprecated You should use bitSize.
unsigned int length;
char _gapC[4];
/// The length of the data in bits
unsigned int bitSize;
/// The data from the sender
unsigned char* data;
@ -49,17 +130,81 @@ struct Packet
bool deleteData;
};
class RakPeerInterface;
/// All RPC functions have the same parameter list - this structure.
struct RPCParameters
{
char _gap0; // TODO: RPCParameters
/// The data from the remote system
unsigned char *input;
/// How many bits long \a input is
unsigned int numberOfBitsOfData;
/// Which system called this RPC
PlayerID sender;
/// Which instance of RakPeer (or a derived RakServer or RakClient) got this call
RakPeerInterface *recipient;
// TODO - RakNet 3.0. Be consistent and have user put ID_TIMESTAMP, rather than do this
// Bug: hasTimestamp was unused.
// bool hasTimestamp;
/// You can return values from RPC calls by writing them to this BitStream.
/// This is only sent back if the RPC call originally passed a BitStream to receive the reply.
/// If you do so and your send is reliable, it will block until you get a reply or you get disconnected from the system you are sending to, whichever is first.
/// If your send is not reliable, it will block for triple the ping time, or until you are disconnected, or you get a reply, whichever is first.
RakNet::BitStream *replyToSender;
};
/// Index of an unassigned player
const PlayerIndex UNASSIGNED_PLAYER_INDEX = 65535;
/// Index of an invalid PlayerID
const PlayerID UNASSIGNED_PLAYER_ID =
{
0xFFFFFFFF, 0xFFFF
};
/// Unassigned object ID
const NetworkID UNASSIGNED_NETWORK_ID =
{
{0xFFFFFFFF, 0xFFFF}, 65535
};
const int PING_TIMES_ARRAY_SIZE = 5;
/// \brief RPC Function Implementation
///
/// The Remote Procedure Call Subsystem provide the RPC paradigm to
/// RakNet user. It consists in providing remote function call over the
/// network. A call to a remote function require you to prepare the
/// data for each parameter (using BitStream) for example.
///
/// Use the following C function prototype for your callbacks
/// @code
/// void functionName(RPCParameters *rpcParms);
/// @endcode
/// If you pass input data, you can parse the input data in two ways.
/// 1.
/// Cast input to a struct (such as if you sent a struct)
/// i.e. MyStruct *s = (MyStruct*) input;
/// Make sure that the sizeof(MyStruct) is equal to the number of bytes passed!
/// 2.
/// Create a BitStream instance with input as data and the number of bytes
/// i.e. BitStream myBitStream(input, (numberOfBitsOfData-1)/8+1)
/// (numberOfBitsOfData-1)/8+1 is how convert from bits to bytes
/// Full example:
/// @code
/// void MyFunc(RPCParameters *rpcParms) {}
/// RakClient *rakClient;
/// REGISTER_AS_REMOTE_PROCEDURE_CALL(rakClient, MyFunc);
/// This would allow MyFunc to be called from the server using (for example)
/// rakServer->RPC("MyFunc", 0, clientID, false);
/// @endcode
/// \def REGISTER_STATIC_RPC
/// \ingroup RAKNET_RPC
/// Register a C function as a Remote procedure.
@ -68,7 +213,28 @@ const PlayerID UNASSIGNED_PLAYER_ID =
/// \attention 12/01/05 REGISTER_AS_REMOTE_PROCEDURE_CALL renamed to REGISTER_STATIC_RPC. Delete the old name sometime in the future
//#pragma deprecated(REGISTER_AS_REMOTE_PROCEDURE_CALL)
//#define REGISTER_AS_REMOTE_PROCEDURE_CALL(networkObject, functionName) REGISTER_STATIC_RPC(networkObject, functionName)
#define REGISTER_STATIC_RPC(networkObject, functionName) (networkObject)->RegisterAsRemoteProcedureCall((RPC_##functionName),(functionName))
#define REGISTER_STATIC_RPC(networkObject, functionName) (networkObject)->RegisterAsRemoteProcedureCall((#functionName),(functionName))
/// \def CLASS_MEMBER_ID
/// \ingroup RAKNET_RPC
/// \brief Concatenate two strings
/// \def REGISTER_CLASS_MEMBER_RPC
/// \ingroup RAKNET_RPC
/// \brief Register a member function of an instantiated object as a Remote procedure call.
/// RPC member Functions MUST be marked __cdecl!
/// \sa ObjectMemberRPC.cpp
/// \b CLASS_MEMBER_ID is a utility macro to generate a unique signature for a class and function pair and can be used for the Raknet functions RegisterClassMemberRPC(...) and RPC(...)
/// \b REGISTER_CLASS_MEMBER_RPC is a utility macro to more easily call RegisterClassMemberRPC
/// \param[in] networkObject Your instance of RakPeer, RakServer, or RakClient
/// \param[in] className The class containing the function
/// \param[in] functionName The name of the function (not in quotes, just the name)
#define CLASS_MEMBER_ID(className, functionName) #className "_" #functionName
#define REGISTER_CLASS_MEMBER_RPC(networkObject, className, functionName) {union {void (__cdecl className::*cFunc)( RPCParameters *rpcParms ); void* voidFunc;}; cFunc=&className::functionName; networkObject->RegisterClassMemberRPC(CLASS_MEMBER_ID(className, functionName),voidFunc);}
/// \def UNREGISTER_AS_REMOTE_PROCEDURE_CALL
/// \depreciated
/// \brief Only calls UNREGISTER_STATIC_RPC
/// \def UNREGISTER_STATIC_RPC
/// \ingroup RAKNET_RPC
@ -79,6 +245,15 @@ const PlayerID UNASSIGNED_PLAYER_ID =
// 12/01/05 UNREGISTER_AS_REMOTE_PROCEDURE_CALL Renamed to UNREGISTER_STATIC_RPC. Delete the old name sometime in the future
//#pragma deprecated(UNREGISTER_AS_REMOTE_PROCEDURE_CALL)
//#define UNREGISTER_AS_REMOTE_PROCEDURE_CALL(networkObject,functionName) UNREGISTER_STATIC_RPC(networkObject,functionName)
#define UNREGISTER_STATIC_RPC(networkObject,functionName) (networkObject)->UnregisterAsRemoteProcedureCall((RPC_##functionName))
#define UNREGISTER_STATIC_RPC(networkObject,functionName) (networkObject)->UnregisterAsRemoteProcedureCall((#functionName))
/// \def UNREGISTER_CLASS_INST_RPC
/// \ingroup RAKNET_RPC
/// \brief Unregisters a member function of an instantiated object as a Remote procedure call.
/// \param[in] networkObject The object that manages the function
/// \param[in] className The className that was originally passed to REGISTER_AS_REMOTE_PROCEDURE_CALL
/// \param[in] functionName The function name
#define UNREGISTER_CLASS_MEMBER_RPC(networkObject, className, functionName) (networkObject)->UnregisterAsRemoteProcedureCall((#className "_" #functionName))
#endif

View File

@ -0,0 +1,19 @@
#include "PacketConsoleLogger.h"
#include "LogCommandParser.h"
PacketConsoleLogger::PacketConsoleLogger()
{
logCommandParser=0;
}
void PacketConsoleLogger::SetLogCommandParser(LogCommandParser *lcp)
{
logCommandParser=lcp;
if (logCommandParser)
logCommandParser->AddChannel("PacketConsoleLogger");
}
void PacketConsoleLogger::WriteLog(const char *str)
{
if (logCommandParser)
logCommandParser->WriteLog("PacketConsoleLogger", str);
}

View File

@ -0,0 +1,37 @@
/// \file
/// \brief This will write all incoming and outgoing network messages to the log command parser, which can be accessed through Telnet
///
/// 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.rakkarsoft.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 __PACKET_CONSOLE_LOGGER_H_
#define __PACKET_CONSOLE_LOGGER_H_
#include "PacketLogger.h"
class LogCommandParser;
/// \ingroup PACKETLOGGER_GROUP
/// \brief Packetlogger that logs to a remote command console
class PacketConsoleLogger : public PacketLogger
{
public:
PacketConsoleLogger();
// Writes to the command parser used for logging, which is accessed through a secondary communication layer (such as Telnet or RakNet) - See ConsoleServer.h
virtual void SetLogCommandParser(LogCommandParser *lcp);
virtual void WriteLog(const char *str);
protected:
LogCommandParser *logCommandParser;
};
#endif

View File

@ -32,128 +32,185 @@
/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream
enum
{
// TODO: PacketEnumerations.h is not complete yet
/// 6: Ping from a connected system. Update timestamps (internal use only)
ID_INTERNAL_PING = 6,
/// 7: Ping from an unconnected system. Reply but do not update timestamps. (internal use only)
//
// RESERVED TYPES - DO NOT CHANGE THESE
//
/// These types are never returned to the user.
/// 0: Ping from a connected system. Update timestamps (internal use only)
ID_INTERNAL_PING,
/// 1: Ping from an unconnected system. Reply but do not update timestamps. (internal use only)
ID_PING,
/// 8: Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only)
/// 2: Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only)
ID_PING_OPEN_CONNECTIONS,
/// 9: Pong from a connected system. Update timestamps (internal use only)
/// 3: Pong from a connected system. Update timestamps (internal use only)
ID_CONNECTED_PONG,
/// 15: Connecting to a secured server/peer
ID_SECURED_CONNECTION_RESPONSE = 15,
/// 16: Connecting to a secured server/peer
/// 4: Someone asked for our static data (internal use only)
ID_REQUEST_STATIC_DATA,
/// 5: Asking for a new connection (internal use only)
ID_CONNECTION_REQUEST,
/// 6: Connecting to a secured server/peer
ID_SECURED_CONNECTION_RESPONSE,
/// 7: Connecting to a secured server/peer
ID_SECURED_CONNECTION_CONFIRMATION,
/// 19: Server / Client only - The server is broadcasting a random number seed (internal use only)
ID_SET_RANDOM_NUMBER_SEED = 19,
/// 20: Remote procedure call (internal use only)
/// 8: Packet that tells us the packet contains an integer ID to name mapping for the remote system
ID_RPC_MAPPING,
/// 9: A reliable packet to detect lost connections
ID_DETECT_LOST_CONNECTIONS,
/// 10: Offline message so we know when to reset and start a new connection
ID_OPEN_CONNECTION_REQUEST,
/// 11: Offline message response so we know when to reset and start a new connection
ID_OPEN_CONNECTION_REPLY,
/// 12: Remote procedure call (internal use only)
ID_RPC,
/// 13: Remote procedure call reply, for RPCs that return data (internal use only)
ID_RPC_REPLY,
/// 14: Server / Client only - The server is broadcasting the pings of all players in the game (internal use only)
ID_BROADCAST_PINGS,
/// 15: Server / Client only - The server is broadcasting a random number seed (internal use only)
ID_SET_RANDOM_NUMBER_SEED,
/// 22: ???
ID_NEW_INCOMING_CONNECTION_2,
//
// USER TYPES - DO NOT CHANGE THESE
//
// Ordered from most useful to least useful
/// [PEER|SERVER|CLIENT] 29: Sent to the player when a connection request cannot be completed due to inability to connect.
/// Never transmitted.
ID_CONNECTION_ATTEMPT_FAILED = 29,
/// [PEER|SERVER] 30: A remote system has successfully connected.
ID_NEW_INCOMING_CONNECTION,
/// [PEER|CLIENT] 31: The system we attempted to connect to is not accepting new connections.
ID_NO_FREE_INCOMING_CONNECTIONS,
/// [PEER|SERVER|CLIENT] 32: The system specified in Packet::playerID has disconnected from us. For the client, this would mean the server has shutdown.
ID_DISCONNECTION_NOTIFICATION,
/// [PEER|SERVER|CLIENT] 33: Reliable packets cannot be delivered to the system specifed in Packet::playerID. The connection to that system has been closed.
ID_CONNECTION_LOST,
/// [PEER|CLIENT] 34: In a client/server environment, our connection request to the server has been accepted.
/// [PEER|CLIENT] 16: In a client/server environment, our connection request to the server has been accepted.
ID_CONNECTION_REQUEST_ACCEPTED,
/// [CLIENT|PEER] 35: We preset an RSA public key which does not match what the system we connected to is using.
/// [PEER|SERVER|CLIENT] 17: Sent to the player when a connection request cannot be completed due to inability to connect.
/// Never transmitted.
ID_CONNECTION_ATTEMPT_FAILED,
/// [PEER|SERVER] 18: A remote system has successfully connected.
ID_NEW_INCOMING_CONNECTION,
/// [PEER|CLIENT] 19: The system we attempted to connect to is not accepting new connections.
ID_NO_FREE_INCOMING_CONNECTIONS,
/// [PEER|SERVER|CLIENT] 20: The system specified in Packet::playerID has disconnected from us. For the client, this would mean the server has shutdown.
ID_DISCONNECTION_NOTIFICATION,
/// [PEER|SERVER|CLIENT] 21: Reliable packets cannot be delivered to the system specifed in Packet::playerID. The connection to that system has been closed.
ID_CONNECTION_LOST,
/// [CLIENT|PEER] 22: We preset an RSA public key which does not match what the system we connected to is using.
ID_RSA_PUBLIC_KEY_MISMATCH,
/// [PEER|CLIENT] 36: We are banned from the system we attempted to connect to.
/// [PEER|CLIENT] 23: We are banned from the system we attempted to connect to.
ID_CONNECTION_BANNED,
/// [PEER|CLIENT] 37: The remote system is using a password and has refused our connection because we did not set the correct password.
/// [PEER|CLIENT] 24: The remote system is using a password and has refused our connection because we did not set the correct password.
ID_INVALID_PASSWORD,
/// [PEER|SERVER|CLIENT] 38: A packet has been tampered with in transit. The sender is contained in Packet::playerID.
/// [PEER|SERVER|CLIENT] 25: A packet has been tampered with in transit. The sender is contained in Packet::playerID.
/// Never transmitted.
ID_MODIFIED_PACKET,
/// [PEER] 39: 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,
/// [PEER|SERVER|CLIENT] 40: 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 StartOccasionalPing.
/// [PEER|SERVER|CLIENT] 26: 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 StartOccasionalPing.
ID_TIMESTAMP,
/// [PEER|SERVER|CLIENT] 41: We got a bitstream containing static data. You can now read this data. This packet is transmitted automatically on connections, and can also be manually sent.
/// [PEER] 27: 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,
/// [PEER|SERVER|CLIENT] 28: We got a bitstream containing static data. You can now read this data. This packet is transmitted automatically on connections, and can also be manually sent.
ID_RECEIVED_STATIC_DATA,
/// [CLIENT] 42: In a client/server environment, a client other than ourselves has disconnected gracefully. Packet::playerID is modified to reflect the playerID of this client.
/// [CLIENT] 29: In a client/server environment, a client other than ourselves has disconnected gracefully. Packet::playerID is modified to reflect the playerID of this client.
ID_REMOTE_DISCONNECTION_NOTIFICATION,
/// [CLIENT] 43: In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::playerID is modified to reflect the playerID of this client.
/// [CLIENT] 30: In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::playerID is modified to reflect the playerID of this client.
ID_REMOTE_CONNECTION_LOST,
/// [FILELIST] 47:
ID_FILE_LIST_TRANSFER_HEADER = 47,
/// [CLIENT] 31: In a client/server environment, a client other than ourselves has connected. Packet::playerID is modified to reflect the playerID of this client.
ID_REMOTE_NEW_INCOMING_CONNECTION,
/// [FILELIST] 48:
/// [CLIENT] 32: On our initial connection to the server, we are told of every other client in the game. Packet::playerID is modified to reflect the playerID of this client.
ID_REMOTE_EXISTING_CONNECTION,
/// [CLIENT] - 33: Got the data for another client
ID_REMOTE_STATIC_DATA,
/// [FILELIST] 34:
ID_FILE_LIST_TRANSFER_HEADER,
/// [FILELIST] 35:
ID_FILE_LIST_TRANSFER_FILE,
/// [Delta Directory transfer] 49:
/// [Delta Directory transfer] 36:
ID_DDT_DOWNLOAD_REQUEST,
/// [PEER|SERVER|CLIENT] 55: Inform a remote system of our IP/Port.
ID_ADVERTISE_SYSTEM = 55,
/// [MASTERSERVER] 37: Request to the master server for the list of servers that contain at least one of the specified keys
ID_QUERY_MASTER_SERVER,
/// [MASTERSERVER] 38: Remove a game server from the master server.
ID_MASTER_SERVER_DELIST_SERVER,
/// [MASTERSERVER|MASTERCLIENT] 39: Add or update the information for a server.
ID_MASTER_SERVER_UPDATE_SERVER,
/// [MASTERSERVER|MASTERCLIENT] 40: Add or set the information for a server.
ID_MASTER_SERVER_SET_SERVER,
/// [MASTERSERVER|MASTERCLIENT] 41: This message indicates a game client is connecting to a game server, and is relayed through the master server.
ID_RELAYED_CONNECTION_NOTIFICATION,
/// [RakNetTransport] 56
/// [PEER|SERVER|CLIENT] 42: Inform a remote system of our IP/Port.
ID_ADVERTISE_SYSTEM,
/// [RakNetTransport] 43
ID_TRANSPORT_STRING,
/// [ConnectionGraph] 57
ID_CONNECTION_GRAPH_REQUEST,
/// [ConnectionGraph] 58
ID_CONNECTION_GRAPH_REPLY,
/// [ConnectionGraph] 59
ID_CONNECTION_GRAPH_UPDATE,
/// [ConnectionGraph] 60
ID_CONNECTION_GRAPH_NEW_CONNECTION,
/// [ConnectionGraph] 61
ID_CONNECTION_GRAPH_CONNECTION_LOST,
/// [ConnectionGraph] 62
ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION,
/// [ReplicaManager] 63
/// [ReplicaManager] 44
ID_REPLICA_MANAGER_CONSTRUCTION,
/// [ReplicaManager] 64
/// [ReplicaManager] 45
ID_REPLICA_MANAGER_DESTRUCTION,
/// [ReplicaManager] 65
/// [ReplicaManager] 46
ID_REPLICA_MANAGER_SCOPE_CHANGE,
/// [ReplicaManager] 66
/// [ReplicaManager] 47
ID_REPLICA_MANAGER_SERIALIZE,
/// [ReplicaManager] 67
/// [ReplicaManager] 48
ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE,
/// [Router] 68
/// [ConnectionGraph] 49
ID_CONNECTION_GRAPH_REQUEST,
/// [ConnectionGraph] 50
ID_CONNECTION_GRAPH_REPLY,
/// [ConnectionGraph] 51
ID_CONNECTION_GRAPH_UPDATE,
/// [ConnectionGraph] 52
ID_CONNECTION_GRAPH_NEW_CONNECTION,
/// [ConnectionGraph] 53
ID_CONNECTION_GRAPH_CONNECTION_LOST,
/// [ConnectionGraph] 54
ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION,
/// [Router] 55
ID_ROUTE_AND_MULTICAST,
/// [NAT Punchthrough]
ID_NAT_PUNCHTHROUGH_REQUEST = 81,
/// [RakVoice] 56
ID_RAKVOICE_OPEN_CHANNEL_REQUEST,
/// [RakVoice] 57
ID_RAKVOICE_OPEN_CHANNEL_REPLY,
/// [RakVoice] 58
ID_RAKVOICE_CLOSE_CHANNEL,
/// [RakVoice] 59
ID_RAKVOICE_DATA,
/// [NAT Punchthrough] Returned to user. PlayerID binary address / port is written to the stream
ID_NAT_TARGET_CONNECTION_LOST,
/// [Autopatcher] 60
ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE,
ID_AUTOPATCHER_CREATION_LIST,
ID_AUTOPATCHER_DELETION_LIST,
ID_AUTOPATCHER_GET_PATCH,
ID_AUTOPATCHER_PATCH_LIST,
ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR, // Returned to user
ID_AUTOPATCHER_FINISHED,
ID_AUTOPATCHER_RESTART_APPLICATION, // Returned to user
/// [NAT Punchthrough]
ID_NAT_PUNCHTHROUGH_REQUEST,
/// [NAT Punchthrough] Returned to user. PlayerID binary address / port is written to the stream
ID_NAT_TARGET_NOT_CONNECTED,
/// [NAT Punchthrough] Returned to user. PlayerID binary address / port is written to the stream
ID_NAT_TARGET_CONNECTION_LOST,
// [NAT Punchthrough]
ID_NAT_CONNECT_AT_TIME,
@ -181,23 +238,12 @@ enum
// 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)
ID_DOWNLOAD_PROGRESS,
///
/// SA-MP 0.3.7 Packet IDs
///
/// Depreciated
ID_RESERVED9,
// For the user to use. Start your first enumeration at this value.
ID_USER_PACKET_ENUM,
//-------------------------------------------------------------------------------------------------------------
ID_VEHICLE_SYNC = 200,
ID_RCON_COMMAND,
ID_RCON_RESPONCE,
ID_AIM_SYNC,
ID_WEAPONS_UPDATE,
ID_STATS_UPDATE,
ID_WEAPON_SHOT_SYNC,
ID_PLAYER_SYNC,
// 208
ID_UNOCCUPIED_SYNC = 209,
ID_TRAILER_SYNC,
ID_PASSENGER_SYNC,
ID_SPECTATOR_SYNC,
};
#endif

View File

@ -0,0 +1,22 @@
#include "PacketFileLogger.h"
#include "GetTime.h"
PacketFileLogger::~PacketFileLogger()
{
fclose(packetLogFile);
}
void PacketFileLogger::OnAttach(RakPeerInterface *peer)
{
PacketLogger::OnAttach(peer);
// Open file for writing
char filename[256];
sprintf(filename, "PacketLog%i.csv", RakNet::GetTime());
packetLogFile = fopen(filename, "wt");
LogHeader();
}
void PacketFileLogger::WriteLog(const char *str)
{
fputs(str, packetLogFile);
}

37
raknet/PacketFileLogger.h Normal file
View File

@ -0,0 +1,37 @@
/// \file
/// \brief This will write all incoming and outgoing network messages to a 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.rakkarsoft.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 __PACKET_FILE_LOGGER_H_
#define __PACKET_FILE_LOGGER_H_
#include "PacketLogger.h"
#include <stdio.h>
/// \ingroup PACKETLOGGER_GROUP
/// \brief Packetlogger that outputs to a file
class PacketFileLogger : public PacketLogger
{
public:
virtual ~PacketFileLogger();
virtual void OnAttach(RakPeerInterface *peer);
virtual void WriteLog(const char *str);
protected:
FILE *packetLogFile;
};
#endif

319
raknet/PacketLogger.cpp Normal file
View File

@ -0,0 +1,319 @@
/// \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.rakkarsoft.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 "PacketLogger.h"
#include "BitStream.h"
#include "DS_List.h"
#include "InternalPacket.h"
#include "RakPeerInterface.h"
#include "PacketEnumerations.h"
#include "StringCompressor.h"
#include "RPCMap.h"
#include "GetTime.h"
#include <stdio.h>
#include <string.h>
#ifdef _MSC_VER
#pragma warning( push )
#endif
PacketLogger::PacketLogger()
{
rakPeer=0;
printId=true;
printAcks=true;
}
PacketLogger::~PacketLogger()
{
}
void PacketLogger::OnAttach(RakPeerInterface *peer)
{
rakPeer=peer;
}
void PacketLogger::OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID)
{
char str[256];
PlayerID localPlayerId;
localPlayerId = rakPeer->GetInternalID();
if (printId==false)
{
sprintf(str, "Snd,Raw, NIL, NIL,%5i,%5i,%i,%u:%i,%u:%i\n", data[0], bitsUsed,RakNet::GetTime(),
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
{
sprintf(str, "Snd,Raw,NIL,NIL,%s,%i,%i,%u:%i,%u:%i\n",IDTOString(data[0]), bitsUsed,RakNet::GetTime(),
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
WriteLog(str);
}
void PacketLogger::LogHeader(void)
{
WriteLog("S|R,Typ,Pckt#,Frm #,PktID,BitLn,Time ,Local IP:Port ,RemoteIP:Port\n");
}
void PacketLogger::OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID)
{
char str[256];
PlayerID localPlayerId;
localPlayerId = rakPeer->GetInternalID();
if (printId==false)
{
sprintf(str, "Rcv,Raw, NIL, NIL,%5i,%5i,%i,%u:%i,%u:%i\n", data[0], bitsUsed,RakNet::GetTime(),
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
{
sprintf(str, "Rcv,Raw,NIL,NIL,%s,%i,%i,%u:%i,%u:%i\n", IDTOString(data[0]), bitsUsed,RakNet::GetTime(),
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
WriteLog(str);
}
void PacketLogger::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend)
{
char str[256];
char sendType[4];
PlayerID localPlayerId;
localPlayerId = rakPeer->GetInternalID();
if (isSend)
strcpy(sendType, "Snd");
else
strcpy(sendType, "Rcv");
// TODO - put this back in a different form
/*
if (internalPacket->isAcknowledgement)
{
if (printAcks)
{
if (printId==false)
sprintf(str, "%s,Ack,%5i,%5i, NIL, 1,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
else
sprintf(str, "%s,Ack,%i,%i,NIL,1,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
str[0]=0;
}
else
*/
{
if (internalPacket->data[0]==ID_TIMESTAMP && internalPacket->data[sizeof(unsigned char)+sizeof(RakNetTime)]!=ID_RPC)
{
if (printId==false)
{
sprintf(str, "%s,Tms,%5i,%5i,%5i,%5i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
internalPacket->data[1+sizeof(int)], internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
{
sprintf(str, "%s,Tms,%i,%i,%s,%i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
IDTOString(internalPacket->data[1+sizeof(int)]), internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
}
else if (internalPacket->data[0]==ID_RPC || (internalPacket->dataBitLength>(sizeof(unsigned char)+sizeof(RakNetTime))*8 && internalPacket->data[0]==ID_TIMESTAMP && internalPacket->data[sizeof(unsigned char)+sizeof(RakNetTime)]==ID_RPC))
{
bool hasTimestamp;
unsigned int bitsOfData;
bool nameIsEncoded;
unsigned char uniqueIdentifier[256];
RPCIndex rpcIndex;
RPCMap *rpcMap;
RakNet::BitStream rpcDecode(internalPacket->data, BITS_TO_BYTES(internalPacket->dataBitLength), false);
rpcDecode.IgnoreBits(8);
if (internalPacket->data[0]==ID_TIMESTAMP)
rpcDecode.IgnoreBits(sizeof(unsigned char)+sizeof(RakNetTime));
rpcDecode.Read(nameIsEncoded);
if (nameIsEncoded)
{
stringCompressor->DecodeString((char*)uniqueIdentifier, 256, &rpcDecode);
}
else
{
rpcDecode.ReadCompressed( rpcIndex );
RPCNode *rpcNode;
rpcMap = rakPeer->GetRPCMap(isSend==true ? remoteSystemID : UNASSIGNED_PLAYER_ID);
if (rpcMap)
rpcNode = rpcMap->GetNodeFromIndex(rpcIndex);
else
rpcNode=0;
if (rpcMap && rpcNode)
strcpy((char*)uniqueIdentifier, rpcNode->uniqueIdentifier);
else
strcpy((char*)uniqueIdentifier, "[UNKNOWN]");
}
rpcDecode.Read(hasTimestamp);
rpcDecode.ReadCompressed(bitsOfData);
if (hasTimestamp)
sprintf(str, "%s,RpT,%5i,%5i,%s,%5i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
uniqueIdentifier, internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
else
sprintf(str, "%s,Rpc,%5i,%5i,%s,%5i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
uniqueIdentifier, internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
{
if (printId==false)
{
sprintf(str, "%s,Nrm,%5i,%5i,%5i,%5i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
internalPacket->data[0], internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
else
{
sprintf(str, "%s,Nrm,%i,%i,%s,%i,%i,%u:%i,%u:%i\n",sendType, internalPacket->messageNumber,frameNumber,
IDTOString(internalPacket->data[0]), internalPacket->dataBitLength,time,
localPlayerId.binaryAddress, localPlayerId.port, remoteSystemID.binaryAddress, remoteSystemID.port);
}
}
}
WriteLog(str);
}
void PacketLogger::WriteLog(const char *str)
{
printf("%s", str);
}
void PacketLogger::SetPrintID(bool print)
{
printId=print;
}
void PacketLogger::SetPrintAcks(bool print)
{
printAcks=print;
}
char* PacketLogger::BaseIDTOString(unsigned char Id)
{
if (Id >= ID_RESERVED9)
return 0;
const char *IDTable[(int)ID_RESERVED9]=
{
"ID_INTERNAL_PING",
"ID_PING",
"ID_PING_OPEN_CONNECTIONS",
"ID_CONNECTED_PONG",
"ID_REQUEST_STATIC_DATA",
"ID_CONNECTION_REQUEST",
"ID_SECURED_CONNECTION_RESPONSE",
"ID_SECURED_CONNECTION_CONFIRMATION",
"ID_RPC_MAPPING",
"ID_DETECT_LOST_CONNECTIONS",
"ID_OPEN_CONNECTION_REQUEST",
"ID_OPEN_CONNECTION_REPLY",
"ID_RPC",
"ID_RPC_REPLY",
"ID_BROADCAST_PINGS",
"ID_SET_RANDOM_NUMBER_SEED",
"ID_CONNECTION_REQUEST_ACCEPTED",
"ID_CONNECTION_ATTEMPT_FAILED",
"ID_NEW_INCOMING_CONNECTION",
"ID_NO_FREE_INCOMING_CONNECTIONS",
"ID_DISCONNECTION_NOTIFICATION",
"ID_CONNECTION_LOST",
"ID_RSA_PUBLIC_KEY_MISMATCH",
"ID_CONNECTION_BANNED",
"ID_INVALID_PASSWORD",
"ID_MODIFIED_PACKET",
"ID_TIMESTAMP",
"ID_PONG",
"ID_RECEIVED_STATIC_DATA",
"ID_REMOTE_DISCONNECTION_NOTIFICATION",
"ID_REMOTE_CONNECTION_LOST",
"ID_REMOTE_NEW_INCOMING_CONNECTION",
"ID_REMOTE_EXISTING_CONNECTION",
"ID_REMOTE_STATIC_DATA",
"ID_FILE_LIST_TRANSFER_HEADER",
"ID_FILE_LIST_TRANSFER_FILE",
"ID_DDT_DOWNLOAD_REQUEST",
"ID_QUERY_MASTER_SERVER",
"ID_MASTER_SERVER_DELIST_SERVER",
"ID_MASTER_SERVER_UPDATE_SERVER",
"ID_MASTER_SERVER_SET_SERVER",
"ID_RELAYED_CONNECTION_NOTIFICATION",
"ID_ADVERTISE_SYSTEM",
"ID_TRANSPORT_STRING",
"ID_REPLICA_MANAGER_CONSTRUCTION",
"ID_REPLICA_MANAGER_DESTRUCTION",
"ID_REPLICA_MANAGER_SCOPE_CHANGE",
"ID_REPLICA_MANAGER_SERIALIZE",
"ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE",
"ID_CONNECTION_GRAPH_REQUEST",
"ID_CONNECTION_GRAPH_REPLY",
"ID_CONNECTION_GRAPH_UPDATE",
"ID_CONNECTION_GRAPH_NEW_CONNECTION",
"ID_CONNECTION_GRAPH_CONNECTION_LOST",
"ID_CONNECTION_GRAPH_DISCONNECTION_NOTIFICATION",
"ID_ROUTE_AND_MULTICAST",
"ID_RAKVOICE_OPEN_CHANNEL_REQUEST",
"ID_RAKVOICE_OPEN_CHANNEL_REPLY",
"ID_RAKVOICE_CLOSE_CHANNEL",
"ID_RAKVOICE_DATA",
"ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE",
"ID_AUTOPATCHER_CREATION_LIST",
"ID_AUTOPATCHER_DELETION_LIST",
"ID_AUTOPATCHER_GET_PATCH",
"ID_AUTOPATCHER_PATCH_LIST",
"ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR",
"ID_AUTOPATCHER_FINISHED",
"ID_AUTOPATCHER_RESTART_APPLICATION",
"ID_NAT_PUNCHTHROUGH_REQUEST",
"ID_NAT_TARGET_NOT_CONNECTED",
"ID_NAT_TARGET_CONNECTION_LOST",
"ID_NAT_CONNECT_AT_TIME",
"ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME",
"ID_DATABASE_QUERY_REQUEST",
"ID_DATABASE_UPDATE_ROW",
"ID_DATABASE_REMOVE_ROW",
"ID_DATABASE_QUERY_REPLY",
"ID_DATABASE_UNKNOWN_TABLE",
"ID_DATABASE_INCORRECT_PASSWORD",
};
return (char*)IDTable[Id];
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
char* PacketLogger::UserIDTOString(unsigned char Id)
{
// Users should override this
return "Unknown";
}
char* PacketLogger::IDTOString(unsigned char Id)
{
char *out;
out=BaseIDTOString(Id);
if (out)
return (char*)out;
return UserIDTOString(Id);
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

65
raknet/PacketLogger.h Normal file
View File

@ -0,0 +1,65 @@
/// \file
/// \brief This will write all incoming and outgoing network messages to the local console screen. See derived functions for other outputs
///
/// 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.rakkarsoft.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 __PACKET_LOGGER_H
#define __PACKET_LOGGER_H
class RakPeerInterface;
#include "NetworkTypes.h"
#include "PluginInterface.h"
#include "Export.h"
/// \defgroup PACKETLOGGER_GROUP PacketLogger
/// \ingroup PLUGINS_GROUP
/// \brief Writes incoming and outgoing messages to the screen.
/// This will write all incoming and outgoing messages to the console window, or to a file if you override it and give it this functionality.
/// \ingroup PACKETLOGGER_GROUP
class RAK_DLL_EXPORT PacketLogger : public PluginInterface
{
public:
PacketLogger();
virtual ~PacketLogger();
virtual void OnAttach(RakPeerInterface *peer);
/// Events on low level sends and receives. These functions may be called from different threads at the same time.
virtual void OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID);
virtual void OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID);
virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend);
/// Logs out a header for all the data
virtual void LogHeader(void);
/// Override this to log strings to wherever. Log should be threadsafe
virtual void WriteLog(const char *str);
// Set to true to print ID_* instead of numbers
virtual void SetPrintID(bool print);
// Print or hide acks (clears up the screen not to print them but is worse for debugging)
virtual void SetPrintAcks(bool print);
protected:
char* IDTOString(unsigned char Id);
char* BaseIDTOString(unsigned char Id);
// Users should override this
virtual char* UserIDTOString(unsigned char Id);
RakPeerInterface *rakPeer;
bool printId, printAcks;
};
#endif

56
raknet/PacketPool.h Normal file
View File

@ -0,0 +1,56 @@
/// \file
/// \brief \b [Internal] A pool for the Packet class. I don't believe this is used any longer.
///
/// 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.rakkarsoft.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.
// TODO - Remove this file from the project
/*
#ifndef __PACKET_POOL
#define __PACKET_POOL
#include "SimpleMutex.h"
#include "DS_Queue.h"
struct Packet;
class PacketPool
{
public:
PacketPool();
~PacketPool();
static void AddReference(void);
static void RemoveReference(void);
Packet* GetPointer( void );
void ReleasePointer( Packet *p );
void ClearPool( void );
static inline PacketPool* Instance()
{
return I;
}
private:
DataStructures::Queue<Packet*> pool;
SimpleMutex poolMutex;
static PacketPool *I;
static int referenceCount;
#ifdef _DEBUG
int packetsReleased;
#endif
};
#endif
*/

View File

@ -32,7 +32,7 @@ enum PacketPriority
/// \note Note to self: I write this with 3 bits in the stream. If I add more remember to change that
enum PacketReliability
{
UNRELIABLE = 6, /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length.
UNRELIABLE, /// Same as regular UDP, except that it will also discard duplicate datagrams. RakNet adds (6 to 17) + 21 bits of overhead, 16 of which is used to detect duplicate packets and 6 to 17 of which is used for message length.
UNRELIABLE_SEQUENCED, /// Regular UDP with a sequence counter. Out of order messages will be discarded. This adds an additional 13 bits on top what is used for UNRELIABLE.
RELIABLE, /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE.
RELIABLE_ORDERED, /// This message is reliable and will arrive in the order you sent it. Messages will be delayed while waiting for out of order messages. Same overhead as UNRELIABLE_SEQUENCED.

View File

@ -0,0 +1,87 @@
/// \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.rakkarsoft.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 "PluginInterface.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnAttach(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnDetach(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnInitialize(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::Update(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
PluginReceiveResult PluginInterface::OnReceive(RakPeerInterface *peer, Packet *packet)
{
return RR_CONTINUE_PROCESSING;
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnDisconnect(RakPeerInterface *peer)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnCloseConnection(RakPeerInterface *peer, PlayerID playerId)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID)
{
}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: <variable name> : unreferenced formal parameter
#endif
void PluginInterface::OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend)
{
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

107
raknet/PluginInterface.h Normal file
View File

@ -0,0 +1,107 @@
/// \file
/// \brief \b RakNet's plugin functionality system. You can derive from this to create your own plugins.
///
/// 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.rakkarsoft.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 __PLUGIN_INTERFACE_H
#define __PLUGIN_INTERFACE_H
class RakPeerInterface;
struct Packet;
struct InternalPacket;
enum PluginReceiveResult
{
// The plugin used this message and it shouldn't be given to the user.
RR_STOP_PROCESSING_AND_DEALLOCATE=0,
// This message will be processed by other plugins, and at last by the user.
RR_CONTINUE_PROCESSING,
// The plugin is going to hold on to this message. Do not deallocate it but do not pass it to other plugins either.
RR_STOP_PROCESSING,
};
#include "NetworkTypes.h"
#include "Export.h"
/// \defgroup PLUGINS_GROUP PluginInterface
/// \brief PluginInterface provides a mechanism to add functionality in a modular way.
/// MessageHandlers should derive from PluginInterface and be attached to RakPeer using the function AttachPlugin
/// On a user call to Receive, OnReceive is called for every PluginInterface, which can then take action based on the message
/// passed to it. This is used to transparently add game-independent functional modules, similar to browser plugins
///
/// \sa ReplicaManager
/// \sa FullyConnectedMesh
/// \sa PacketLogger
/// \ingroup PLUGINS_GROUP
class RAK_DLL_EXPORT PluginInterface
{
public:
/// Called when the interface is attached
/// \param[in] peer the instance of RakPeer that is calling Receive
virtual void OnAttach(RakPeerInterface *peer);
/// Called when the interface is detached
/// \param[in] peer the instance of RakPeer that is calling Receive
virtual void OnDetach(RakPeerInterface *peer);
/// Called when RakPeer is initialized
/// \param[in] peer the instance of RakPeer that is calling Receive
virtual void OnInitialize(RakPeerInterface *peer);
/// Update is called every time a packet is checked for .
/// \param[in] peer - the instance of RakPeer that is calling Receive
virtual void Update(RakPeerInterface *peer);
/// OnReceive is called for every packet.
/// \param[in] peer the instance of RakPeer that is calling Receive
/// \param[in] packet the packet that is being returned to the user
/// \return True to allow the game and other plugins to get this message, false to absorb it
virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet);
/// Called when RakPeer is shutdown
/// \param[in] peer the instance of RakPeer that is calling Receive
virtual void OnDisconnect(RakPeerInterface *peer);
/// Called when a connection is dropped because the user called RakPeer::CloseConnection() for a particular system
/// \param[in] peer the instance of RakPeer that is calling Receive
/// \param[in] playerId The system whose connection was closed
virtual void OnCloseConnection(RakPeerInterface *peer, PlayerID playerId);
/// Called on a send to the socket, per datagram, that does not go through the reliability layer
/// \param[in] data The data being sent
/// \param[in] bitsUsed How many bits long \a data is
/// \param[in] remoteSystemID Which system this message is being sent to
virtual void OnDirectSocketSend(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID);
/// Called on a receive from the socket, per datagram, that does not go through the reliability layer
/// \param[in] data The data being sent
/// \param[in] bitsUsed How many bits long \a data is
/// \param[in] remoteSystemID Which system this message is being sent to
virtual void OnDirectSocketReceive(const char *data, const unsigned bitsUsed, PlayerID remoteSystemID);
/// Called on a send or recieve within the reliability layer
/// \param[in] internalPacket The user message, along with all send data.
/// \param[in] frameNumber The number of frames sent or received so far for this player depending on \a isSend . Indicates the frame of this user message.
/// \param[in] remoteSystemID The player we sent or got this packet from
/// \param[in] time The current time as returned by RakNet::GetTime()
/// \param[in] isSend Is this callback representing a send event or receive event?
virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, PlayerID remoteSystemID, RakNetTime time, bool isSend);
};
#endif

View File

@ -1,16 +1,167 @@
// TODO: Implement RPCMap.cpp
/// \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.rakkarsoft.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 "RPCMap.h"
#include <string.h>
RPCMap::RPCMap()
{
}
RPCMap::~RPCMap()
{
Clear();
}
void RPCMap::Clear(void)
{
unsigned i;
RPCNode *node;
for (i=0; i < rpcSet.Size(); i++)
{
node=rpcSet[i];
if (node)
{
delete [] node->uniqueIdentifier;
delete node;
}
}
rpcSet.Clear();
}
RPCNode *RPCMap::GetNodeFromIndex(RPCIndex index)
{
if ((unsigned)index < rpcSet.Size())
return rpcSet[(unsigned)index];
return 0;
}
RPCNode *RPCMap::GetNodeFromFunctionName(char *uniqueIdentifier)
{
unsigned index;
index=(unsigned)GetIndexFromFunctionName(uniqueIdentifier);
if ((RPCIndex)index!=UNDEFINED_RPC_INDEX)
return rpcSet[index];
return 0;
}
RPCIndex RPCMap::GetIndexFromFunctionName(char *uniqueIdentifier)
{
unsigned index;
for (index=0; index < rpcSet.Size(); index++)
if (rpcSet[index] && strcmp(rpcSet[index]->uniqueIdentifier, uniqueIdentifier)==0)
return (RPCIndex) index;
return UNDEFINED_RPC_INDEX;
}
// Called from the user thread for the local system
void RPCMap::AddIdentifierWithFunction(unsigned char uniqueIdentifier, void *functionPointer, bool isPointerToMember)
void RPCMap::AddIdentifierWithFunction(char *uniqueIdentifier, void *functionPointer, bool isPointerToMember)
{
#ifdef _DEBUG
assert(rpcSet.Size()+1 < MAX_RPC_MAP_SIZE); // If this hits change the typedef of RPCIndex to use an unsigned short
assert(uniqueIdentifier && uniqueIdentifier[0]);
assert(functionPointer);
#endif
unsigned index, existingNodeIndex;
RPCNode *node;
existingNodeIndex=GetIndexFromFunctionName(uniqueIdentifier);
if ((RPCIndex)existingNodeIndex!=UNDEFINED_RPC_INDEX) // Insert at any free spot.
{
// Trying to insert an identifier at any free slot and that identifier already exists
// The user should not insert nodes that already exist in the list
#ifdef _DEBUG
// assert(0);
#endif
return;
}
node = new RPCNode;
node->uniqueIdentifier = uniqueIdentifier;
node->uniqueIdentifier = new char [strlen(uniqueIdentifier)+1];
strcpy(node->uniqueIdentifier, uniqueIdentifier);
node->functionPointer=functionPointer;
node->isPointerToMember=isPointerToMember;
rpcSet[uniqueIdentifier] = node;
// Insert into an empty spot if possible
for (index=0; index < rpcSet.Size(); index++)
{
if (rpcSet[index]==0)
{
rpcSet.Replace(node, 0, index);
return;
}
}
rpcSet.Insert(node); // No empty spots available so just add to the end of the list
}
void RPCMap::AddIdentifierAtIndex(char *uniqueIdentifier, RPCIndex insertionIndex)
{
#ifdef _DEBUG
assert(uniqueIdentifier && uniqueIdentifier[0]);
#endif
unsigned existingNodeIndex;
RPCNode *node, *oldNode;
existingNodeIndex=GetIndexFromFunctionName(uniqueIdentifier);
if (existingNodeIndex==insertionIndex)
return; // Already there
if ((RPCIndex)existingNodeIndex!=UNDEFINED_RPC_INDEX)
{
// Delete the existing one
oldNode=rpcSet[existingNodeIndex];
rpcSet[existingNodeIndex]=0;
delete [] oldNode->uniqueIdentifier;
delete oldNode;
}
node = new RPCNode;
node->uniqueIdentifier = new char [strlen(uniqueIdentifier)+1];
strcpy(node->uniqueIdentifier, uniqueIdentifier);
node->functionPointer=0;
// Insert at a user specified spot
if (insertionIndex < rpcSet.Size())
{
// Overwrite what is there already
oldNode=rpcSet[insertionIndex];
if (oldNode)
{
delete [] oldNode->uniqueIdentifier;
delete oldNode;
}
rpcSet[insertionIndex]=node;
}
else
{
// Insert after the end of the list and use 0 as a filler for the empty spots
rpcSet.Replace(node, 0, insertionIndex);
}
}
void RPCMap::RemoveNode(char *uniqueIdentifier)
{
unsigned index;
index=GetIndexFromFunctionName(uniqueIdentifier);
#ifdef _DEBUG
assert(index!=UNDEFINED_RPC_INDEX); // If this hits then the user was removing an RPC call that wasn't currently registered
#endif
RPCNode *node;
node = rpcSet[index];
delete [] node->uniqueIdentifier;
delete node;
rpcSet[index]=0;
}

View File

@ -1,19 +1,48 @@
// TODO: Implement RPCMap.h
/// \file
/// \brief \b [Internal] A container class for a list of RPCNodes
///
/// \ingroup RAKNET_RPC
///
/// 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.rakkarsoft.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 __RPC_MAP
#define __RPC_MAP
#include "RPCNode.h"
#include "DS_List.h"
#include "NetworkTypes.h"
#include "Export.h"
#define RPC_MAP_SIZE 256
/// \ingroup RAKNET_RPC
/// \internal
/// \brief A container class for a list of RPCNodes
struct RAK_DLL_EXPORT RPCMap
{
public:
void AddIdentifierWithFunction(unsigned char uniqueIdentifier, void *functionPointer, bool isPointerToMember);
RPCMap();
~RPCMap();
void Clear(void);
RPCNode *GetNodeFromIndex(RPCIndex index);
RPCNode *GetNodeFromFunctionName(char *uniqueIdentifier);
RPCIndex GetIndexFromFunctionName(char *uniqueIdentifier);
void AddIdentifierWithFunction(char *uniqueIdentifier, void *functionPointer, bool isPointerToMember);
void AddIdentifierAtIndex(char *uniqueIdentifier, RPCIndex insertionIndex);
void RemoveNode(char *uniqueIdentifier);
protected:
RPCNode *rpcSet[RPC_MAP_SIZE];
DataStructures::List<RPCNode *> rpcSet;
};
#endif

View File

@ -1,4 +1,21 @@
// TODO: Implement RPCNode.h
/// \file
/// \brief \b [Internal] Holds information related to a RPC
///
/// \ingroup RAKNET_RPC
///
/// 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.rakkarsoft.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 __RPC_NODE
#define __RPC_NODE
@ -6,6 +23,9 @@
#include "NetworkTypes.h"
#include "Export.h"
class RakPeerInterface;
/// \defgroup RAKNET_RPC Remote Procedure Call Subsystem
/// \brief A system to call C or object member procudures on other systems, and even to return return values.
@ -16,8 +36,9 @@
///
struct RAK_DLL_EXPORT RPCNode
{
/// String identifier of the RPC
unsigned char uniqueIdentifier;
char *uniqueIdentifier;
/// Force casting of member functions to void *
union
@ -37,3 +58,4 @@ struct RAK_DLL_EXPORT RPCNode
};
#endif

1255
raknet/RSACrypt.h Normal file

File diff suppressed because it is too large Load Diff

8
raknet/RakAssert.h Normal file
View File

@ -0,0 +1,8 @@
#include <assert.h>
// So stupid Linux doesn't assert in release
#ifdef _DEBUG
#define RakAssert(x) assert(x);
#else
#define RakAssert(x)
#endif

View File

@ -1,122 +1,353 @@
// TODO: Implement RakClient.cpp
/// \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.rakkarsoft.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 "RakClient.h"
#include "PacketEnumerations.h"
#include "GetTime.h"
#ifdef _MSC_VER
#pragma warning( push )
#endif
// Constructor
RakClient::RakClient()
{
// TODO: RakClient::RakClient saco .text:10034BA0 server L .text:08067B60 bot W .text:004033E0 L .text:0806B130
unsigned i;
for ( i = 0; i < 32; i++ )
otherClients[ i ].isActive = false;
nextSeedUpdate = 0;
}
void RakClient::vftable_0()
// Destructor
RakClient::~RakClient()
{}
#ifdef _MSC_VER
#pragma warning( disable : 4100 ) // warning C4100: 'depreciated' : unreferenced formal parameter
#endif
bool RakClient::Connect( const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer )
{
// TODO: RakClient::vftable_0() (saco 10034A30) (server L: 80691F0) (bot W: 403270 L: 806CBF2)
RakPeer::Disconnect( 100 );
RakPeer::Initialize( 1, clientPort, threadSleepTimer );
if ( host[ 0 ] < '0' || host[ 0 ] > '2' )
{
#if !defined(_COMPATIBILITY_1)
host = ( char* ) SocketLayer::Instance()->DomainNameToIP( host );
#else
return false;
#endif
}
void RakClient::vftable_4()
unsigned i;
for ( i = 0; i < 32; i++ )
{
// TODO: RakClient::vftable_4() (saco 10034130) (server L: 8069200) (bot W: 402970 L: 806CC00)
otherClients[ i ].isActive = false;
otherClients[ i ].playerId = UNASSIGNED_PLAYER_ID;
otherClients[ i ].staticData.Reset();
}
// ignore depreciated. A pointless variable
return RakPeer::Connect( host, serverPort, ( char* ) password.GetData(), password.GetNumberOfBytesUsed() );
}
void RakClient::Disconnect( unsigned int blockDuration, unsigned char orderingChannel )
{
// TODO: RakClient::vftable_8() (saco 10034A40) (server L: 8069210) (bot W: 403280 L: 806CC0E)
RakPeer::Disconnect( blockDuration, orderingChannel );
}
void RakClient::vftable_C()
void RakClient::InitializeSecurity( const char *privKeyP, const char *privKeyQ )
{
// TODO: RakClient::vftable_C() (saco 10034200) (server L: 8069220) (bot W: 402A40 L: 806CC36)
RakPeer::InitializeSecurity( privKeyP, privKeyQ, 0, 0 );
}
void RakClient::vftable_10()
void RakClient::SetPassword( const char *_password )
{
// TODO: RakClient::vftable_10() (saco 10034220) (server L: 8069230) (bot W: 402A60 L: 806CC44)
if ( _password == 0 || _password[ 0 ] == 0 )
password.Reset();
else
{
password.Reset();
password.Write( _password, ( int ) strlen( _password ) + 1 );
}
}
void RakClient::vftable_14()
bool RakClient::HasPassword( void ) const
{
// TODO: RakClient::vftable_14() (saco 10034270) (server L: 8069240) (bot W: 402AB0 L: 806CC52)
return password.GetNumberOfBytesUsed() > 0;
}
void RakClient::vftable_18()
bool RakClient::Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel )
{
// TODO: RakClient::vftable_18() (saco 100342E0) (server L: 8069250) (bot W: 402B20 L: 806CC60)
if ( remoteSystemList == 0 )
return false;
return RakPeer::Send( data, length, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false );
}
void RakClient::vftable_1C()
bool RakClient::Send( RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel )
{
// TODO: RakClient::vftable_1C() (saco 10034290) (server L: 8069260) (bot W: 402AD0 L: 806CC6E)
if ( remoteSystemList == 0 )
return false;
return RakPeer::Send( bitStream, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false );
}
void RakClient::vftable_20()
Packet* RakClient::Receive( void )
{
// TODO: RakClient::vftable_20() (saco 10035200) (server L: 8069270) (bot W: 403A40 L: 806CC7C)
Packet * packet = RakPeer::Receive();
// Intercept specific client / server feature packets
if ( packet )
{
RakNet::BitStream bitStream( packet->data, packet->length, false );
int i;
if ( packet->data[ 0 ] == ID_CONNECTION_REQUEST_ACCEPTED )
{
// ConnectionAcceptStruct cas;
// cas.Deserialize(bitStream);
// unsigned short remotePort;
// PlayerID externalID;
PlayerIndex playerIndex;
RakNet::BitStream inBitStream(packet->data, packet->length, false);
inBitStream.IgnoreBits(8); // ID_CONNECTION_REQUEST_ACCEPTED
inBitStream.IgnoreBits(8 * sizeof(unsigned short)); //inBitStream.Read(remotePort);
inBitStream.IgnoreBits(8 * sizeof(unsigned int)); //inBitStream.Read(externalID.binaryAddress);
inBitStream.IgnoreBits(8 * sizeof(unsigned short)); //inBitStream.Read(externalID.port);
inBitStream.Read(playerIndex);
localPlayerIndex = playerIndex;
packet->playerIndex = playerIndex;
}
else if (
packet->data[ 0 ] == ID_REMOTE_NEW_INCOMING_CONNECTION ||
packet->data[ 0 ] == ID_REMOTE_EXISTING_CONNECTION ||
packet->data[ 0 ] == ID_REMOTE_DISCONNECTION_NOTIFICATION ||
packet->data[ 0 ] == ID_REMOTE_CONNECTION_LOST )
{
bitStream.IgnoreBits( 8 ); // Ignore identifier
bitStream.Read( packet->playerId.binaryAddress );
bitStream.Read( packet->playerId.port );
if ( bitStream.Read( ( unsigned short& ) packet->playerIndex ) == false )
{
DeallocatePacket( packet );
return 0;
}
void RakClient::vftable_24()
if ( packet->data[ 0 ] == ID_REMOTE_DISCONNECTION_NOTIFICATION ||
packet->data[ 0 ] == ID_REMOTE_CONNECTION_LOST )
{
// TODO: RakClient::vftable_24() (saco 10034A50) (server L: 8069280) (bot W: 403290 L: 806CC8A)
i = GetOtherClientIndexByPlayerID( packet->playerId );
if ( i >= 0 )
otherClients[ i ].isActive = false;
}
}
else if ( packet->data[ 0 ] == ID_REMOTE_STATIC_DATA )
{
bitStream.IgnoreBits( 8 ); // Ignore identifier
bitStream.Read( packet->playerId.binaryAddress );
bitStream.Read( packet->playerId.port );
bitStream.Read( packet->playerIndex ); // ADDED BY KURI
i = GetOtherClientIndexByPlayerID( packet->playerId );
if ( i < 0 )
i = GetFreeOtherClientIndex();
if ( i >= 0 )
{
otherClients[ i ].playerId = packet->playerId;
otherClients[ i ].isActive = true;
otherClients[ i ].staticData.Reset();
// The static data is what is left over in the stream
otherClients[ i ].staticData.Write( ( char* ) bitStream.GetData() + BITS_TO_BYTES( bitStream.GetReadOffset() ), bitStream.GetNumberOfBytesUsed() - BITS_TO_BYTES( bitStream.GetReadOffset() ) );
}
}
else if ( packet->data[ 0 ] == ID_BROADCAST_PINGS )
{
PlayerID playerId;
int index;
bitStream.IgnoreBits( 8 ); // Ignore identifier
for ( i = 0; i < 32; i++ )
{
if ( bitStream.Read( playerId.binaryAddress ) == false )
break; // No remaining data!
bitStream.Read( playerId.port );
index = GetOtherClientIndexByPlayerID( playerId );
if ( index >= 0 )
bitStream.Read( otherClients[ index ].ping );
else
{
index = GetFreeOtherClientIndex();
if ( index >= 0 )
{
otherClients[ index ].isActive = true;
bitStream.Read( otherClients[ index ].ping );
otherClients[ index ].playerId = playerId;
otherClients[ index ].staticData.Reset();
}
void RakClient::vftable_28()
{
// TODO: RakClient::vftable_28() (saco 10034370) (server L: 8069290) (bot W: 402BB0 L: 806CCA4)
else
bitStream.IgnoreBits( sizeof( short ) * 8 );
}
}
void RakClient::vftable_2C()
DeallocatePacket( packet );
return 0;
}
else
if ( packet->data[ 0 ] == ID_TIMESTAMP &&
packet->length == sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned char)+sizeof(unsigned int)+sizeof(unsigned int) )
{
// TODO: RakClient::vftable_2C() (saco 10034340) (server L: 80692A0) (bot W: 402B80 L: 806CCB2)
RakNet::BitStream inBitStream(packet->data, packet->length, false);
RakNetTime timeStamp;
unsigned char typeId;
unsigned int in_seed;
unsigned int in_nextSeed;
inBitStream.IgnoreBits(8); // ID_TIMESTAMP
inBitStream.Read(timeStamp);
inBitStream.Read(typeId); // ID_SET_RANDOM_NUMBER_SEED ?
// Check to see if this is a user TIMESTAMP message which
// accidentally has length SetRandomNumberSeedStruct_Size
if ( typeId != ID_SET_RANDOM_NUMBER_SEED )
return packet;
inBitStream.Read(in_seed);
inBitStream.Read(in_nextSeed);
seed = in_seed;
nextSeed = in_nextSeed;
nextSeedUpdate = timeStamp + 9000; // Seeds are updated every 9 seconds
DeallocatePacket( packet );
return 0;
}
}
void RakClient::vftable_30()
{
// TODO: RakClient::vftable_30() (saco 100343B0) (server L: 80692B0) (bot W: 402BF0 L: 806CCC0)
return packet;
}
void RakClient::vftable_34()
void RakClient::DeallocatePacket( Packet *packet )
{
// TODO: RakClient::vftable_34() (saco 100343E0) (server L: 80692C0) (bot W: 402C20 L: 806CCCE)
RakPeer::DeallocatePacket( packet );
}
void RakClient::vftable_38()
void RakClient::PingServer( void )
{
// TODO: RakClient::vftable_38() (saco 10034410) (server L: 80692D0) (bot W: 402C50 L: 806CCDC)
if ( remoteSystemList == 0 )
return ;
RakPeer::Ping( remoteSystemList[ 0 ].playerId );
}
void RakClient::vftable_3C()
void RakClient::PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections )
{
// TODO: RakClient::vftable_3C() (saco 10034440) (server L: 80692E0) (bot W: 402C80 L: 806CCEA)
RakPeer::Initialize( 1, clientPort, 0 );
RakPeer::Ping( host, serverPort, onlyReplyOnAcceptingConnections );
}
void RakClient::vftable_40()
int RakClient::GetAveragePing( void )
{
// TODO: RakClient::vftable_40() (saco 10034490) (server L: 80692F0) (bot W: 402CD0 L: 806CCF8)
if ( remoteSystemList == 0 )
return -1;
return RakPeer::GetAveragePing( remoteSystemList[ 0 ].playerId );
}
void RakClient::vftable_44()
int RakClient::GetLastPing( void ) const
{
// TODO: RakClient::vftable_44() (saco 100344A0) (server L: 8069300) (bot W: 402CE0 L: 806CD06)
if ( remoteSystemList == 0 )
return -1;
return RakPeer::GetLastPing( remoteSystemList[ 0 ].playerId );
}
int RakClient::GetLowestPing( void ) const
{
if ( remoteSystemList == 0 )
return -1;
return RakPeer::GetLowestPing( remoteSystemList[ 0 ].playerId );
}
int RakClient::GetPlayerPing( const PlayerID playerId )
{
int i;
for ( i = 0; i < 32; i++ )
if ( otherClients[ i ].playerId == playerId )
return otherClients[ i ].ping;
return -1;
}
void RakClient::StartOccasionalPing( void )
{
RakPeer::SetOccasionalPing( true );
}
void RakClient::StopOccasionalPing( void )
{
RakPeer::SetOccasionalPing( false );
}
bool RakClient::IsConnected( void ) const
{
// TODO: RakClient::vftable_48() (saco 100344B0) (server L: 8069310) (bot W: 402CF0 L: 806CD14)
return false;
unsigned short numberOfSystems;
RakPeer::GetConnectionList( 0, &numberOfSystems );
return numberOfSystems == 1;
}
void RakClient::vftable_4C()
unsigned int RakClient::GetSynchronizedRandomInteger( void ) const
{
// TODO: RakClient::vftable_4C() (saco 100344D0) (server L: 8069320) (bot W: 402D10 L: 806CD22)
if ( RakNet::GetTime() > nextSeedUpdate )
return nextSeed;
else
return seed;
}
void RakClient::vftable_50()
bool RakClient::GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer )
{
// TODO: RakClient::vftable_50() (saco 10034A60) (server L: 8069330) (bot W: 4032A0 L: 806CD30)
return RakPeer::GenerateCompressionLayer( inputFrequencyTable, inputLayer );
}
void RakClient::vftable_54()
bool RakClient::DeleteCompressionLayer( bool inputLayer )
{
// TODO: RakClient::vftable_54() (saco 10034A70) (server L: 8069340) (bot W: 4032B0 L: 806CD3E)
return RakPeer::DeleteCompressionLayer( inputLayer );
}
void RakClient::RegisterAsRemoteProcedureCall( char* uniqueID, void ( *functionPointer ) ( RPCParameters *rpcParms ) )
@ -134,158 +365,243 @@ void RakClient::UnregisterAsRemoteProcedureCall( char* uniqueID )
RakPeer::UnregisterAsRemoteProcedureCall( uniqueID );
}
void RakClient::vftable_64()
bool RakClient::RPC( char* uniqueID, const char *data, unsigned int bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget )
{
// TODO: RakClient::vftable_64() (saco 10034620) (server L: 8069380) (bot W: 402E60 L: 806CD76)
if ( remoteSystemList == 0 )
return false;
return RakPeer::RPC( uniqueID, data, bitLength, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false, shiftTimestamp, networkID, replyFromTarget );
}
void RakClient::vftable_68()
bool RakClient::RPC( char* uniqueID, RakNet::BitStream *parameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget )
{
// TODO: RakClient::vftable_68() (saco 100345B0) (server L: 8069390) (bot W: 402DF0 L: 806CD84)
if ( remoteSystemList == 0 )
return false;
return RakPeer::RPC( uniqueID, parameters, priority, reliability, orderingChannel, remoteSystemList[ 0 ].playerId, false, shiftTimestamp, networkID, replyFromTarget );
}
void RakClient::vftable_6C()
void RakClient::SetTrackFrequencyTable( bool b )
{
// TODO: RakClient::vftable_6C() (saco 10034540) (server L: 80693A0) (bot W: 402D80 L: 806CD92)
RakPeer::SetCompileFrequencyTable( b );
}
void RakClient::vftable_70()
bool RakClient::GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] )
{
// TODO: RakClient::vftable_70() (saco 10034690) (server L: 80693B0) (bot W: 402ED0 L: 806CDA0)
return RakPeer::GetOutgoingFrequencyTable( outputFrequencyTable );
}
void RakClient::vftable_74()
float RakClient::GetCompressionRatio( void ) const
{
// TODO: RakClient::vftable_74() (saco 100346A0) (server L: 80693C0) (bot W: 402EE0 L: 806CDAE)
return RakPeer::GetCompressionRatio();
}
void RakClient::vftable_78()
float RakClient::GetDecompressionRatio( void ) const
{
// TODO: RakClient::vftable_78() (saco 10034AB0) (server L: 80693D0) (bot W: 4032F0 L: 806CDBC)
return RakPeer::GetDecompressionRatio();
}
void RakClient::vftable_7C()
void RakClient::AttachPlugin( PluginInterface *messageHandler )
{
// TODO: RakClient::vftable_7C() (saco 10034AC0) (server L: 80693E0) (bot W: 403300 L: 806CDCA)
RakPeer::AttachPlugin(messageHandler);
}
void RakClient::vftable_80()
void RakClient::DetachPlugin( PluginInterface *messageHandler )
{
// TODO: RakClient::vftable_80() (saco 10034AD0) (server L: 80693F0) (bot W: 403310 L: 806CDD8)
RakPeer::DetachPlugin(messageHandler);
}
void RakClient::vftable_84()
RakNet::BitStream * RakClient::GetStaticServerData( void )
{
// TODO: RakClient::vftable_84() (saco 10034AE0) (server L: 8069400) (bot W: 403320 L: 806CDE6)
if ( remoteSystemList == 0 )
return 0;
return RakPeer::GetRemoteStaticData( remoteSystemList[ 0 ].playerId );
}
void RakClient::vftable_88()
void RakClient::SetStaticServerData( const char *data, const int length )
{
// TODO: RakClient::vftable_88() (saco 100346F0) (server L: 8069410) (bot W: 402F30 L: 806CDF4)
if ( remoteSystemList == 0 )
return ;
RakPeer::SetRemoteStaticData( remoteSystemList[ 0 ].playerId, data, length );
}
void RakClient::vftable_8C()
RakNet::BitStream * RakClient::GetStaticClientData( const PlayerID playerId )
{
// TODO: RakClient::vftable_8C() (saco 10034720) (server L: 8069420) (bot W: 402F60 L: 806CE02)
int i;
if ( playerId == UNASSIGNED_PLAYER_ID )
{
return & localStaticData;
}
void RakClient::vftable_90()
else
{
// TODO: RakClient::vftable_90() (saco 100350E0) (server L: 8069430) (bot W: 403920 L: 806CE10)
i = GetOtherClientIndexByPlayerID( playerId );
if ( i >= 0 )
{
return & ( otherClients[ i ].staticData );
}
void RakClient::vftable_94()
{
// TODO: RakClient::vftable_94() (saco 10035140) (server L: 8069440) (bot W: 403980 L: 806CE1E)
}
void RakClient::vftable_98()
{
// TODO: RakClient::vftable_98() (saco 10034760) (server L: 8069450) (bot W: 402FA0 L: 806CE2C)
return 0;
}
void RakClient::vftable_9C()
void RakClient::SetStaticClientData( const PlayerID playerId, const char *data, const int length )
{
// TODO: RakClient::vftable_9C() (saco 10034790) (server L: 8069460) (bot W: 402FD0 L: 806CE3A)
int i;
if ( playerId == UNASSIGNED_PLAYER_ID )
{
localStaticData.Reset();
localStaticData.Write( data, length );
}
void RakClient::vftable_A0()
else
{
// TODO: RakClient::vftable_A0() (saco 100347D0) (server L: 8069470) (bot W: 403010 L: 806CE48)
i = GetOtherClientIndexByPlayerID( playerId );
if ( i >= 0 )
{
otherClients[ i ].staticData.Reset();
otherClients[ i ].staticData.Write( data, length );
}
void RakClient::vftable_A4()
{
// TODO: RakClient::vftable_A4() (saco 10034AF0) (server L: 8069480) (bot W: 403330 L: 806CE56)
else
RakPeer::SetRemoteStaticData( playerId, data, length );
}
void RakClient::vftable_A8()
{
// TODO: RakClient::vftable_A8() (saco 10034B00) (server L: 8069490) (bot W: 403340 L: 806CE64)
}
void RakClient::vftable_AC()
void RakClient::SendStaticClientDataToServer( void )
{
// TODO: RakClient::vftable_AC() (saco 10034B10) (server L: 80694A0) (bot W: 403350 L: 806CE72)
if ( remoteSystemList == 0 )
return ;
RakPeer::SendStaticData( remoteSystemList[ 0 ].playerId );
}
void RakClient::vftable_B0()
PlayerID RakClient::GetServerID( void ) const
{
// TODO: RakClient::vftable_B0() (saco 10034B20) (server L: 80694B0) (bot W: 403360 L: 806CE80)
if ( remoteSystemList == 0 )
return UNASSIGNED_PLAYER_ID;
return remoteSystemList[ 0 ].playerId;
}
void RakClient::vftable_B4()
PlayerID RakClient::GetPlayerID( void ) const
{
// TODO: RakClient::vftable_B4() (saco 10034B30) (server L: 80694C0) (bot W: 403370 L: 806CE8E)
if ( remoteSystemList == 0 )
return UNASSIGNED_PLAYER_ID;
// GetExternalID is more accurate because it reflects our external IP and port to the server.
// GetInternalID only matches the parameters we passed
PlayerID myID = RakPeer::GetExternalID( remoteSystemList[ 0 ].playerId );
if ( myID == UNASSIGNED_PLAYER_ID )
return RakPeer::GetInternalID();
else
return myID;
}
void RakClient::vftable_B8()
PlayerID RakClient::GetInternalID( void ) const
{
// TODO: RakClient::vftable_B8() (saco 100348E0) (server L: 80694D0) (bot W: 403120 L: 806CE9C)
return RakPeer::GetInternalID();
}
void RakClient::vftable_BC()
const char* RakClient::PlayerIDToDottedIP( const PlayerID playerId ) const
{
// TODO: RakClient::vftable_BC() (saco 10034B40) (server L: 80694E0) (bot W: 403380 L: 806CEAA)
return RakPeer::PlayerIDToDottedIP( playerId );
}
void RakClient::vftable_C0()
void RakClient::PushBackPacket( Packet *packet, bool pushAtHead )
{
// TODO: RakClient::vftable_C0() (saco 10034B50) (server L: 80694F0) (bot W: 403390 L: 806CEB8)
RakPeer::PushBackPacket(packet, pushAtHead);
}
void RakClient::vftable_C4()
void RakClient::SetRouterInterface( RouterInterface *routerInterface )
{
// TODO: RakClient::vftable_C4() (saco 10034B60) (server L: 8069500) (bot W: 4033A0 L: 806CEC6)
RakPeer::SetRouterInterface(routerInterface);
}
void RakClient::RemoveRouterInterface( RouterInterface *routerInterface )
{
RakPeer::RemoveRouterInterface(routerInterface);
}
void RakClient::vftable_C8()
void RakClient::SetTimeoutTime( RakNetTime timeMS )
{
// TODO: RakClient::vftable_C8() (saco 10034B70) (server L: 8069510) (bot W: 4033B0 L: 806CED4)
RakPeer::SetTimeoutTime( timeMS, GetServerID() );
}
void RakClient::vftable_CC()
bool RakClient::SetMTUSize( int size )
{
// TODO: RakClient::vftable_CC() (saco 10034960) (server L: 8069520) (bot W: 4031A0 L: 806CEE2)
return RakPeer::SetMTUSize( size );
}
void RakClient::vftable_D0()
int RakClient::GetMTUSize( void ) const
{
// TODO: RakClient::vftable_D0() (saco 10034B80) (server L: 8069530) (bot W: 4033C0 L: 806CEF0)
return RakPeer::GetMTUSize();
}
void RakClient::vftable_D4()
void RakClient::AllowConnectionResponseIPMigration( bool allow )
{
// TODO: RakClient::vftable_D4() (saco 10034B90) (server L: 8069540) (bot W: 4033D0 L: 806CEFE)
RakPeer::AllowConnectionResponseIPMigration( allow );
}
void RakClient::vftable_D8()
void RakClient::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength )
{
// TODO: RakClient::vftable_D8() (saco 10034A20) (server L: 8069550) (bot W: 403260 L: 806CF0C)
RakPeer::AdvertiseSystem( host, remotePort, data, dataLength );
}
void RakClient::vftable_DC()
RakNetStatisticsStruct* const RakClient::GetStatistics( void )
{
// TODO: RakClient::vftable_DC() (bot L: 0806CF1A)
return RakPeer::GetStatistics( remoteSystemList[ 0 ].playerId );
}
void RakClient::ApplyNetworkSimulator( double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance)
{
RakPeer::ApplyNetworkSimulator( maxSendBPS, minExtraPing, extraPingVariance );
}
bool RakClient::IsNetworkSimulatorActive( void )
{
return RakPeer::IsNetworkSimulatorActive();
}
int RakClient::GetOtherClientIndexByPlayerID( const PlayerID playerId )
{
unsigned i;
for ( i = 0; i < 32; i++ )
{
if ( otherClients[ i ].playerId == playerId )
return i;
}
return -1;
}
int RakClient::GetFreeOtherClientIndex( void )
{
unsigned i;
for ( i = 0; i < 32; i++ )
{
if ( otherClients[ i ].isActive == false )
return i;
}
return -1;
}
PlayerIndex RakClient::GetPlayerIndex( void )
{
return localPlayerIndex;
}
#ifdef _MSC_VER
#pragma warning( pop )
#endif

View File

@ -1,4 +1,21 @@
// TODO: Implement RakClient.h
/// \file
/// \brief Specializes RakPeer to act as a client.
///
/// \remarks This file originated from my first game, where I only had a server and a client. Now that I have RakPeer, it's depreciated although in such wide use I can't delete it.
///
/// 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.rakkarsoft.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 __RAK_CLIENT_H
#define __RAK_CLIENT_H
@ -6,6 +23,11 @@
#include "RakPeer.h"
#include "RakClientInterface.h"
// #pragma deprecated(RakClient)
/// This is a user-interface class to act as a game client. All it does is implement some functionality on top of RakPeer.
/// See the individual functions for what the class can do.
/// \brief Defines the functions used by a game client
class RakClient : public RakPeer, public RakClientInterface
{
@ -13,8 +35,19 @@ public:
///Constructor
RakClient();
void vftable_0();
void vftable_4();
/// Destructor
virtual ~RakClient();
///Call this to connect the client to the specified host (ip or domain name) and server port.
/// This is a non-blocking connection. You know the connection is successful when IsConnected() returns true
/// or receive gets a packet with the type identifier ID_CONNECTION_REQUEST_ACCEPTED.
/// \param[in] host Dotted IP or a domain name
/// \param[in] serverPort The port on which to connect to \a host
/// \param[in] clientPort The port to use localy
/// \param[in] depreciated Ignore this
/// \param[in] threadSleepTimer How many ms to Sleep each internal update cycle (30 to give the game priority, 0 for regular (recommended), -1 to not Sleep() (may be slower))
/// \return true on successful initiation, false otherwise
bool Connect( const char* host, unsigned short serverPort, unsigned short clientPort, unsigned int depreciated, int threadSleepTimer );
/// Stops the client, stops synchronized data, and resets all internal data.
/// Does nothing if the client is not connected to begin wit
@ -22,29 +55,132 @@ public:
/// \param[in] orderingChannel If blockDuration > 0, the disconnect packet will be sent on this channel
void Disconnect( unsigned int blockDuration, unsigned char orderingChannel=0 );
void vftable_C();
void vftable_10();
void vftable_14();
void vftable_18();
void vftable_1C();
void vftable_20();
void vftable_24();
void vftable_28();
void vftable_2C();
void vftable_30();
void vftable_34();
void vftable_38();
void vftable_3C();
void vftable_40();
void vftable_44();
/// Can be called to use specific public RSA keys. (e and n)
/// In order to prevent altered keys.
/// Will return ID_RSA_PUBLIC_KEY_MISMATCH in a packet if a key has been altered.
/// \param[in] privKeyP Private keys generated from the RSACrypt class. Can be 0
/// \param[in] privKeyQ Private keys generated from the RSACrypt class. Can be 0
/// \sa Encryption.cpp
void InitializeSecurity( const char *privKeyP, const char *privKeyQ );
/// Set the password to use when connecting to a server. The password persists between connections.
/// \param[in] _password The password to use to connect to a server, or 0 for none.
void SetPassword( const char *_password );
/// Returns true if a password was set, false otherwise
/// \return true if a password has previously been set using SetPassword
bool HasPassword( void ) const;
/// Sends a block of data to the specified system that you are connected to.
/// This function only works while the connected (Use the Connect function).
/// \param[in] data The block of data to send
/// \param[in] length The size in bytes of the data to send
/// \param[in] priority What priority level to send on.
/// \param[in] reliability How reliability to send this data
/// \param[in] orderingChannel When using ordered or sequenced packets, what channel to order these on.- Packets are only ordered relative to other packets on the same stream
/// \return False if we are not connected to the specified recipient. True otherwise
bool Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel );
/// Sends a block of data to the specified system that you are connected to.
/// This function only works while the connected (Use the Connect function).
/// \param[in] bitStream The bitstream to send
/// \param[in] priority What priority level to send on.
/// \param[in] reliability How reliability to send this data
/// \param[in] orderingChannel When using ordered or sequenced packets, what channel to order these on.- Packets are only ordered relative to other packets on the same stream
/// \return False if we are not connected to the specified recipient. True otherwise
bool Send( RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel );
/// Gets a packet from the incoming packet queue.
/// Use DeallocatePacket() to deallocate the packet after you are done with it.
/// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here.
/// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet.
// sa CoreNetworkStructures.h contains struct Packet
Packet* Receive( void );
/// Call this to deallocate a packet returned by Receive when you are done handling it.
/// \param[in] packet The packet to deallocate.
void DeallocatePacket( Packet *packet );
/// Send a ping to the server
void PingServer( void );
/// Sends a ping request to a server we are not connected to. This will also initialize the
/// networking system if it is not already initialized. You can stop the networking system
/// by calling Disconnect()
/// The final ping time will be encoded in the following sizeof(RakNetTime) bytes. (Default is 4 bytes - See __GET_TIME_64BIT in NetworkTypes.h
/// You can specify if the server should only reply if it has an open connection or not
/// This must be true for LAN broadcast server discovery on "255.255.255.255"
/// or you will get replies from clients as well.
/// \param[in] host The host to contact
/// \param[in] ServerPort the port used by the server
/// \param[in] clientPort the port used to receive the answer
/// \param[in] onlyReplyOnAcceptingConnections if true the server must be ready to accept incomming connection.
void PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections );
/// Returns the average of all ping times read.
int GetAveragePing( void );
/// Returns the last ping time read
/// \return last ping value
int GetLastPing( void ) const;
/// Returns the lowest ping time read or -1 if none read yet
/// \return lowest ping value
int GetLowestPing( void ) const;
/// Returns the last ping for the specified player. This information
/// is broadcast by the server automatically In order to save
/// bandwidth this information is updated only infrequently and only
/// for the first 32 players
/// \param[in] playerId The id of the player you want to have the ping (it might be your id)
/// \return the last ping for this player
/// \note You can read your own ping with
/// this method by passing your own playerId, however for more
/// up-to-date readings you should use one of the three functions
/// above
int GetPlayerPing( const PlayerID playerId );
/// Ping the server every so often. This is on by default.
/// In games where you don't care about ping you can callStopOccasionalPing to save the bandwidth.
/// This can be called anytime.
void StartOccasionalPing( void );
/// Stop pinging the server every so often. The server is pinged by
/// default. In games where you don't care about ping you can call
/// this to save the bandwidth This will work anytime
void StopOccasionalPing( void );
/// Returns true if the client is connected to a responsive server
/// \return true if connected to a server
bool IsConnected( void ) const;
void vftable_4C();
void vftable_50();
void vftable_54();
/// Returns a number automatically synchronized between the server and client which randomly changes every 9 seconds.
/// The time it changes is accurate to within a few ms and is best used to seed random number generators that you want to usually
/// return the same output on all systems.
/// Keep in mind thisisn't perfectly accurate as there is always a very small chance the numbers will by out of synch.
/// You should should confine its use to visual effects or functionality that has a backup method to maintain synchronization.
/// If you don't need this functionality and want to save the bandwidth callStopSynchronizedRandomInteger after starting the server
/// \return A number, which is probably synchronized among all clients and the server.
unsigned int GetSynchronizedRandomInteger( void ) const;
/// This is an optional function to generate the compression layer based on the input frequency table.
/// If you want to use it you should call this twice - once with inputLayer as true and once as false.
/// The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false.
/// Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true.
/// Calling this function when there is an existing layer will overwrite the old layer.
/// \pre You should only call this when disconnected
/// \param[in] inputFrequencyTable A frequency table for your data
/// \param[in] inputLayer Is this the input layer?
/// \return false (failure) if connected. Otherwise true (success)
/// \sa Compression.cpp
bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer );
/// Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory.
/// \pre You should only call this when disconnected
/// \param[in] inputLayer True to mean the inputLayer, false to mean the output layer
/// \return false (failure) if connected. Otherwise true (success)
bool DeleteCompressionLayer( bool inputLayer );
/// \ingroup RAKNET_RPC
/// Register a C or static member function as available for calling as a remote procedure call
@ -64,40 +200,237 @@ public:
/// \param[in] uniqueID A string of only letters to identify this procedure. Recommended you use the macro CLASS_MEMBER_ID for class member functions. Must match the parameterpassed to RegisterAsRemoteProcedureCall
void UnregisterAsRemoteProcedureCall( char* uniqueID );
void vftable_64();
void vftable_68();
void vftable_6C();
void vftable_70();
void vftable_74();
void vftable_78();
void vftable_7C();
void vftable_80();
void vftable_84();
void vftable_88();
void vftable_8C();
void vftable_90();
void vftable_94();
void vftable_98();
void vftable_9C();
void vftable_A0();
void vftable_A4();
void vftable_A8();
void vftable_AC();
void vftable_B0();
void vftable_B4();
void vftable_B8();
void vftable_BC();
void vftable_C0();
void vftable_C4();
void vftable_C8();
void vftable_CC();
void vftable_D0();
void vftable_D4();
void vftable_D8();
void vftable_DC();
/// \ingroup RAKNET_RPC
/// Calls a C function on the remote system that was already registered using RegisterAsRemoteProcedureCall.
/// If you want that function to return data you should call RPC from that system in the same wayReturns true on a successful packet
/// send (this does not indicate the recipient performed the call), false on failure
/// \param[in] uniqueID A NULL terimianted string to this procedure. Recommended you use the macro CLASS_MEMBER_ID for class member functions. Must match the parameter
/// \param[in] data The data to send
/// \param[in] bitLength The number of bits of \a data
/// \param[in] priority What priority level to send on.
/// \param[in] reliability How reliability to send this data
/// \param[in] orderingChannel When using ordered or sequenced packets, what channel to order these on.
/// \param[in] shiftTimestamp True to add a timestamp to your data, such that the first byte is ID_TIMESTAMP and the next sizeof(RakNetTime) is the timestamp.
/// \param[in] networkID For static functions, pass UNASSIGNED_NETWORK_ID. For member functions, you must derive from NetworkIDGenerator and pass the value returned by NetworkIDGenerator::GetNetworkID for that object.
/// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which is remtely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed.
/// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure\note This is part of the Remote Procedure Call Subsystem
bool RPC( char* uniqueID, const char *data, unsigned int bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget );
/// \ingroup RAKNET_RPC
/// Calls a C function on the remote system that was already registered using RegisterAsRemoteProcedureCall.
/// If you want that function to return data you should call RPC from that system in the same wayReturns true on a successful packet
/// send (this does not indicate the recipient performed the call), false on failure
/// \param[in] uniqueID A NULL terimianted string to this procedure. Recommended you use the macro CLASS_MEMBER_ID for class member functions. Must match the parameter
/// \param[in] bitStream The bitstream to send
/// \param[in] priority What priority level to send on.
/// \param[in] reliability How reliability to send this data
/// \param[in] orderingChannel When using ordered or sequenced packets, what channel to order these on.
/// \param[in] shiftTimestamp True to add a timestamp to your data, such that the first byte is ID_TIMESTAMP and the next sizeof(RakNetTime) is the timestamp.
/// \param[in] networkID For static functions, pass UNASSIGNED_NETWORK_ID. For member functions, you must derive from NetworkIDGenerator and pass the value returned by NetworkIDGenerator::GetNetworkID for that object.
/// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which is remtely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed.
/// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure\note This is part of the Remote Procedure Call Subsystem
bool RPC( char* uniqueID, RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, bool shiftTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget );
/// Enables or disables frequency table tracking. This is required to get a frequency table, which is used in GenerateCompressionLayer()
/// This value persists between connect calls and defaults to false (no frequency tracking)
/// \pre You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only trackpart of the values sent over the network.
/// \param[in] b True to enable tracking
void SetTrackFrequencyTable( bool b );
/// Returns the frequency of outgoing bytes into outputFrequencyTable. This is required to get a frequency table, which is used in GenerateCompressionLayer()
/// The purpose is to save to file as either a master frequency table from a sample game session.
/// \pre You should only call this when disconnected
/// \pre Requires that you first enable data frequency tracking by calling SetTrackFrequencyTable(true)
/// \param[out] outputFrequencyTable The Frequency Table used in the compression layer
/// \return false (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success)
bool GetSendFrequencyTable( unsigned int outputFrequencyTable[ 256 ] );
/// Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data
/// \return The compression ratio
float GetCompressionRatio( void ) const;
///Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data
/// \return The decompression ratio
float GetDecompressionRatio( void ) const;
/// Attatches a Plugin interface to run code automatically on message receipt in the Receive call
/// \note If plugins have dependencies on each other then the order does matter - for example the router plugin should go first because it might route messages for other plugins
/// \param[in] messageHandler Pointer to a message handler to attach
void AttachPlugin( PluginInterface *messageHandler );
///Detaches a Plugin interface to run code automatically on message receipt
/// \param[in] messageHandler Pointer to a message handler to detach
void DetachPlugin( PluginInterface *messageHandler );
/// The server internally maintains a data struct that is
/// automatically sent to clients when the connect. This is useful
/// to contain data such as the server name or message of the day.
/// Access that struct with this function. The data is entered as an
/// array and stored and returned as a BitStream. Everytime you call
/// GetStaticServerData it resets the read pointer to the start of
/// the bitstream. To do multiple reads without reseting the pointer
/// Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...);
/// To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods
/// of the bitstream for the 2nd and 3rd parameters
/// Note that the server may change at any time the
/// data contents and/or its length!
/// \return a bitstream containing static server data
RakNet::BitStream * GetStaticServerData( void );
/// The server internally maintains a data struct that is
/// automatically sent to clients when the connect. This is useful
/// to contain data such as the server name or message of the day.
/// Access that struct with this function. The data is entered as an
/// array and stored and returned as a BitStream. Everytime you call
/// GetStaticServerData it resets the read pointer to the start of
/// the bitstream. To do multiple reads without reseting the pointer
/// Maintain a pointer copy to the bitstream as in RakNet::BitStream *copy = ...->GetStaticServerData(...);
/// To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods
/// of the bitstream for the 2nd and 3rd parameters
/// Note that the server may change at any time the
/// data contents and/or its length!
void SetStaticServerData( const char *data, const int length );
/// The client internally maintains a data struct that is automatically sent to the server on connection
/// This is useful to contain data such as the player name. Access that struct with this
/// function. Pass UNASSIGNED_PLAYER_ID for playerId to reference your internal data. A playerId value to access the data of another player.
/// *** NOTE ** * If you change any data in the struct the server won't reflect this change unless you manually update it
/// Do so by calling SendStaticClientDataToServer
/// The data is entered as an array and stored and returned as a BitStream.
/// Everytime you call GetStaticServerData it resets the read pointer to the start of the bitstream. To do multiple reads without reseting the pointer
/// Maintain a pointer copy to the bitstream as in
/// RakNet::BitStream *copy = ...->GetStaticServerData(...);
/// To store a bitstream, use the GetData() and GetNumberOfBytesUsed() methods
/// of the bitstream for the 2nd and 3rd parameters
RakNet::BitStream * GetStaticClientData( const PlayerID playerId );
/// Set Local statistical information for playId. Call this
/// function when you receive statistical information from a
/// client.
/// \param[in] playerId the player ID
/// \param[in] data the packet data
/// \param[in] length the size of the data
void SetStaticClientData( const PlayerID playerId, const char *data, const int length );
/// Send the static server data to the server The only time you need
/// to call this function is to update clients that are already
/// connected when you change the static server data by calling
/// GetStaticServerData and directly modifying the object pointed to.
/// Obviously if the connected clients don't need to know the new
/// data you don't need to update them, so it's up to you The server
/// must be active for this to have meaning
void SendStaticClientDataToServer( void );
/// Return the player number of the server.
/// \return the server playerID
PlayerID GetServerID( void ) const;
/// Return the player number the server has assigned to you.
/// \return our player ID
/// \note that unlike in previous versions, this is a struct and is not sequential
PlayerID GetPlayerID( void ) const;
///Return the unique address identifier that represents you on the the network and is based on your local IP / portNote that unlike in previous versions, this is a struct and is not sequential
PlayerID GetInternalID( void ) const;
/// Returns the dotted IP address for the specified playerId
/// \param[in] playerId Any player ID other than UNASSIGNED_PLAYER_ID, even if that player is not currently connected
/// \return a dotted notation string representation of the address of playerId.
const char* PlayerIDToDottedIP( const PlayerID playerId ) const;
/// Put a packet back at the end of the receive queue in case you don't want to deal with it immediately
/// \param[in] packet the packet to delayed
/// \pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order)
void PushBackPacket( Packet *packet, bool pushAtHead );
/// \Internal
void SetRouterInterface( RouterInterface *routerInterface );
/// \Internal
void RemoveRouterInterface( RouterInterface *routerInterface );
/// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet
/// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug.
/// \param[in] timeMS Time, in MS
void SetTimeoutTime( RakNetTime timeMS );
/// Set the MTU per datagram. It's important to set this correctly - otherwise packets will be needlessly split, decreasing performance and throughput.
/// Maximum allowed size is MAXIMUM_MTU_SIZE.
/// Too high of a value will cause packets not to arrive at worst and be fragmented at best.
/// Too low of a value will split packets unnecessarily.
/// sa MTUSize.h
/// \pre Can only be called when not connected.
/// \return false on failure (we are connected), else true
bool SetMTUSize( int size );
/// Returns the current MTU size
/// \return The current MTU size
int GetMTUSize( void ) const;
///Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary
/// when connection to servers with multiple IP addresses.
/// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections
void AllowConnectionResponseIPMigration( bool allow );
/// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system.
/// This will tell the remote system our external IP outside the LAN along with some user data.
/// \pre The sender and recipient must already be started via a successful call to Initialize
/// \param[in] host Either a dotted IP address or a domain name
/// \param[in] remotePort Which port to connect to on the remote machine.
/// \param[in] data Optional data to append to the packet.
/// \param[in] dataLength length of data in bytes. Use 0 if no data.
void AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength );
/// Returns a structure containing a large set of network statistics for server
/// You can map this data to a string using the C style StatisticsToString() function
/// \sa RakNetStatistics.h
RakNetStatisticsStruct * const GetStatistics( void );
/// Adds simulated ping and packet loss to the outgoing data flow.
/// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each.
/// You can exclude network simulator code with the _RELEASE #define to decrease code size
/// \param[in] maxSendBPS The maximum window size for the windowing algorithm before 100% packetloss. The chance of each packet being lost is RakNetStatisticsStruct::windowSize / maxSendBPS . Recommended you set this between 100 and 1000 with lower numbers corresponding to greater packetloss. This approximates the real condition where the more data you send the more likely you are to lose data. Call with 0 to disable.
/// \param[in] minExtraPing The minimum time to delay sends.
/// \param[in] extraPingVariance The additional random time to delay sends.
void ApplyNetworkSimulator( double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance);
/// Returns if you previously called ApplyNetworkSimulator
/// \return If you previously called ApplyNetworkSimulator
bool IsNetworkSimulatorActive( void );
/// @internal
/// Retrieve the player index corresponding to this client.
PlayerIndex GetPlayerIndex( void );
private:
int GetOtherClientIndexByPlayerID( const PlayerID playerId );
int GetFreeOtherClientIndex( void );
RakNet::BitStream password;
struct OtherClientsStruct
{
///The id of the other player
PlayerID playerId;
///The average ping time
short ping;
///Other client's Static Client Data
RakNet::BitStream staticData;
///Tel whether the remote client is active or not
bool isActive;
}
otherClients[ 32 ];
unsigned int seed;
unsigned int nextSeed;
RakNetTime nextSeedUpdate;
PlayerIndex localPlayerIndex;
PlayerID externalPlayerID;
};
#endif

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