mirror of
https://github.com/dashr9230/SA-MP.git
synced 2024-12-22 14:37:29 +08:00
Revert RakNet source files back to the original v2.518 state
* Add RakNet source files to the VS project
This commit is contained in:
parent
3bad4d20c2
commit
bcdbedc0be
360
bot/bot.vcproj
360
bot/bot.vcproj
@ -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>
|
||||
|
323
raknet/AsynchronousFileIO.cpp
Normal file
323
raknet/AsynchronousFileIO.cpp
Normal 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
|
||||
*/
|
91
raknet/AsynchronousFileIO.h
Normal file
91
raknet/AsynchronousFileIO.h
Normal 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
|
||||
*/
|
15
raknet/AutopatcherPatchContext.h
Normal file
15
raknet/AutopatcherPatchContext.h
Normal 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
|
52
raknet/AutopatcherRepositoryInterface.h
Normal file
52
raknet/AutopatcherRepositoryInterface.h
Normal 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
1820
raknet/BigTypes.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -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;
|
||||
@ -14,8 +62,8 @@ BitStream::BitStream()
|
||||
readOffset = 0;
|
||||
//data = ( unsigned char* ) malloc( 32 );
|
||||
data = ( unsigned char* ) stackData;
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
#ifdef _DEBUG
|
||||
// assert( data );
|
||||
#endif
|
||||
//memset(data, 0, 32);
|
||||
@ -49,42 +97,7 @@ BitStream::BitStream( unsigned char* _data, unsigned int lengthInBytes, bool _co
|
||||
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;
|
||||
}
|
||||
|
||||
// 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 )
|
||||
@ -115,7 +128,7 @@ void BitStream::SetNumberOfBitsAllocated( const unsigned int lengthInBits )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
assert( lengthInBits >= ( unsigned int ) numberOfBitsAllocated );
|
||||
#endif
|
||||
#endif
|
||||
numberOfBitsAllocated = lengthInBits;
|
||||
}
|
||||
|
||||
@ -130,23 +143,640 @@ void BitStream::Reset( void )
|
||||
// Note: Do NOT reallocate memory because BitStream is used
|
||||
// in places to serialize/deserialize a buffer. Reallocation
|
||||
// is a dangerous operation (may result in leaks).
|
||||
|
||||
|
||||
if ( numberOfBitsUsed > 0 )
|
||||
{
|
||||
// memset(data, 0, BITS_TO_BYTES(numberOfBitsUsed));
|
||||
}
|
||||
|
||||
|
||||
// Don't free memory here for speed efficiency
|
||||
//free(data); // Use realloc and free so we are more efficient than delete and new for resizing
|
||||
numberOfBitsUsed = 0;
|
||||
|
||||
|
||||
//numberOfBitsAllocated=8;
|
||||
readOffset = 0;
|
||||
|
||||
|
||||
//data=(unsigned char*)malloc(1);
|
||||
// if (numberOfBitsAllocated>0)
|
||||
// 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
|
||||
|
1385
raknet/BitStream.h
1385
raknet/BitStream.h
File diff suppressed because it is too large
Load Diff
1875
raknet/BitStream_NoTemplate.cpp
Normal file
1875
raknet/BitStream_NoTemplate.cpp
Normal file
File diff suppressed because it is too large
Load Diff
713
raknet/BitStream_NoTemplate.h
Normal file
713
raknet/BitStream_NoTemplate.h
Normal 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
97
raknet/CheckSum.cpp
Normal 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
52
raknet/CheckSum.h
Normal 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
|
50
raknet/ClientContextStruct.h
Normal file
50
raknet/ClientContextStruct.h
Normal 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
|
174
raknet/CommandParserInterface.cpp
Normal file
174
raknet/CommandParserInterface.cpp
Normal 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
|
||||
|
148
raknet/CommandParserInterface.h
Normal file
148
raknet/CommandParserInterface.h
Normal 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
623
raknet/ConnectionGraph.cpp
Normal 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
165
raknet/ConnectionGraph.h
Normal 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
273
raknet/ConsoleServer.cpp
Normal 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
62
raknet/ConsoleServer.h
Normal 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
1157
raknet/DS_BPlusTree.h
Normal file
File diff suppressed because it is too large
Load Diff
1145
raknet/DS_BinarySearchTree.h
Normal file
1145
raknet/DS_BinarySearchTree.h
Normal file
File diff suppressed because it is too large
Load Diff
101
raknet/DS_ByteQueue.cpp
Normal file
101
raknet/DS_ByteQueue.cpp
Normal 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
45
raknet/DS_ByteQueue.h
Normal 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
250
raknet/DS_Heap.h
Normal 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
|
306
raknet/DS_HuffmanEncodingTree.cpp
Normal file
306
raknet/DS_HuffmanEncodingTree.cpp
Normal 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
|
70
raknet/DS_HuffmanEncodingTree.h
Normal file
70
raknet/DS_HuffmanEncodingTree.h
Normal 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
|
60
raknet/DS_HuffmanEncodingTreeFactory.h
Normal file
60
raknet/DS_HuffmanEncodingTreeFactory.h
Normal 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
|
30
raknet/DS_HuffmanEncodingTreeNode.h
Normal file
30
raknet/DS_HuffmanEncodingTreeNode.h
Normal 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
1258
raknet/DS_LinkedList.h
Normal file
File diff suppressed because it is too large
Load Diff
417
raknet/DS_List.h
Normal file
417
raknet/DS_List.h
Normal 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
314
raknet/DS_Map.h
Normal 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
90
raknet/DS_MemoryPool.h
Normal 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
|
252
raknet/DS_OrderedChannelHeap.h
Normal file
252
raknet/DS_OrderedChannelHeap.h
Normal 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
236
raknet/DS_OrderedList.h
Normal 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
415
raknet/DS_Queue.h
Normal 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
110
raknet/DS_QueueLinkedList.h
Normal 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
218
raknet/DS_RangeList.h
Normal 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
772
raknet/DS_Table.cpp
Normal 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
285
raknet/DS_Table.h
Normal 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
97
raknet/DS_Tree.h
Normal 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
537
raknet/DS_WeightedGraph.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -1,18 +1,69 @@
|
||||
// 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
|
||||
{
|
||||
|
||||
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
58
raknet/DataCompressor.cpp
Normal 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
33
raknet/DataCompressor.h
Normal 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
|
227
raknet/DirectoryDeltaTransfer.cpp
Normal file
227
raknet/DirectoryDeltaTransfer.cpp
Normal 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
|
124
raknet/DirectoryDeltaTransfer.h
Normal file
124
raknet/DirectoryDeltaTransfer.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
1
raknet/EncodeClassName.cpp
Normal file
1
raknet/EncodeClassName.cpp
Normal file
@ -0,0 +1 @@
|
||||
|
67
raknet/ExtendedOverlappedPool.cpp
Normal file
67
raknet/ExtendedOverlappedPool.cpp
Normal 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
|
||||
|
||||
*/
|
50
raknet/ExtendedOverlappedPool.h
Normal file
50
raknet/ExtendedOverlappedPool.h
Normal 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
592
raknet/FileList.cpp
Normal 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
51
raknet/FileList.h
Normal 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
408
raknet/FileListTransfer.cpp
Normal 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
89
raknet/FileListTransfer.h
Normal 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
|
38
raknet/FileListTransferCBInterface.h
Normal file
38
raknet/FileListTransferCBInterface.h
Normal 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
147
raknet/FileOperations.cpp
Normal 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
12
raknet/FileOperations.h
Normal 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
|
98
raknet/FullyConnectedMesh.cpp
Normal file
98
raknet/FullyConnectedMesh.cpp
Normal 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
|
54
raknet/FullyConnectedMesh.h
Normal file
54
raknet/FullyConnectedMesh.h
Normal 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
|
@ -48,22 +48,22 @@ RakNetTime RakNet::GetTime( void )
|
||||
#else
|
||||
gettimeofday( &initialTime, 0 );
|
||||
#endif
|
||||
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER PerfVal;
|
||||
|
||||
|
||||
QueryPerformanceCounter( &PerfVal );
|
||||
|
||||
|
||||
return (RakNetTime)(PerfVal.QuadPart*1000 / yo.QuadPart);
|
||||
#else
|
||||
gettimeofday( &tp, 0 );
|
||||
|
||||
|
||||
// Seconds to ms and microseconds to ms
|
||||
return ( tp.tv_sec - initialTime.tv_sec ) * 1000 + ( tp.tv_usec - initialTime.tv_usec ) / 1000;
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
81
raknet/InternalPacket.h
Normal file
81
raknet/InternalPacket.h
Normal 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
|
||||
|
67
raknet/InternalPacketPool.cpp
Normal file
67
raknet/InternalPacketPool.cpp
Normal 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();
|
||||
}
|
||||
|
55
raknet/InternalPacketPool.h
Normal file
55
raknet/InternalPacketPool.h
Normal 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
|
||||
|
131
raknet/LightweightDatabaseClient.cpp
Normal file
131
raknet/LightweightDatabaseClient.cpp
Normal 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
|
88
raknet/LightweightDatabaseClient.h
Normal file
88
raknet/LightweightDatabaseClient.h
Normal 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
|
45
raknet/LightweightDatabaseCommon.cpp
Normal file
45
raknet/LightweightDatabaseCommon.cpp
Normal 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);
|
||||
}
|
49
raknet/LightweightDatabaseCommon.h
Normal file
49
raknet/LightweightDatabaseCommon.h
Normal 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
|
147
raknet/LightweightDatabaseServer.h
Normal file
147
raknet/LightweightDatabaseServer.h
Normal 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
19
raknet/LinuxStrings.cpp
Normal 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
12
raknet/LinuxStrings.h
Normal 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
276
raknet/LogCommandParser.cpp
Normal 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
117
raknet/LogCommandParser.h
Normal 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
|
@ -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
19
raknet/Makefile
Normal 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
106
raknet/Makefile.am
Normal 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
381
raknet/NatPunchthrough.cpp
Normal 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
143
raknet/NatPunchthrough.h
Normal 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
|
291
raknet/NetworkIDGenerator.cpp
Normal file
291
raknet/NetworkIDGenerator.cpp
Normal 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
134
raknet/NetworkIDGenerator.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
19
raknet/PacketConsoleLogger.cpp
Normal file
19
raknet/PacketConsoleLogger.cpp
Normal 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);
|
||||
}
|
37
raknet/PacketConsoleLogger.h
Normal file
37
raknet/PacketConsoleLogger.h
Normal 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
|
@ -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,
|
||||
|
||||
@ -180,24 +237,13 @@ 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
|
||||
///
|
||||
|
||||
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,
|
||||
|
||||
/// Depreciated
|
||||
ID_RESERVED9,
|
||||
// For the user to use. Start your first enumeration at this value.
|
||||
ID_USER_PACKET_ENUM,
|
||||
//-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
22
raknet/PacketFileLogger.cpp
Normal file
22
raknet/PacketFileLogger.cpp
Normal 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
37
raknet/PacketFileLogger.h
Normal 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
319
raknet/PacketLogger.cpp
Normal 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
65
raknet/PacketLogger.h
Normal 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
56
raknet/PacketPool.h
Normal 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
|
||||
|
||||
*/
|
@ -16,7 +16,7 @@
|
||||
/// option) any later version.
|
||||
|
||||
#ifndef __PACKET_PRIORITY_H
|
||||
#define __PACKET_PRIORITY_H
|
||||
#define __PACKET_PRIORITY_H
|
||||
|
||||
/// These enumerations are used to describe when packets are delivered.
|
||||
enum PacketPriority
|
||||
@ -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.
|
||||
|
87
raknet/PluginInterface.cpp
Normal file
87
raknet/PluginInterface.cpp
Normal 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
107
raknet/PluginInterface.h
Normal 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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,20 +23,24 @@
|
||||
#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.
|
||||
|
||||
/// \ingroup RAKNET_RPC
|
||||
/// \ingroup RAKNET_RPC
|
||||
/// \internal
|
||||
///
|
||||
/// \brief Map registered procedure inside of a peer.
|
||||
///
|
||||
///
|
||||
/// \brief Map registered procedure inside of a peer.
|
||||
///
|
||||
struct RAK_DLL_EXPORT RPCNode
|
||||
{
|
||||
/// String identifier of the RPC
|
||||
unsigned char uniqueIdentifier;
|
||||
|
||||
/// Force casting of member functions to void *
|
||||
|
||||
/// String identifier of the RPC
|
||||
char *uniqueIdentifier;
|
||||
|
||||
/// Force casting of member functions to void *
|
||||
union
|
||||
{
|
||||
void ( *staticFunctionPointer ) ( RPCParameters *rpcParms );
|
||||
@ -31,9 +52,10 @@ struct RAK_DLL_EXPORT RPCNode
|
||||
|
||||
void *functionPointer;
|
||||
};
|
||||
|
||||
|
||||
/// Is this a member function pointer? True if so. If false it's a regular C function.
|
||||
bool isPointerToMember;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
1255
raknet/RSACrypt.h
Normal file
1255
raknet/RSACrypt.h
Normal file
File diff suppressed because it is too large
Load Diff
8
raknet/RakAssert.h
Normal file
8
raknet/RakAssert.h
Normal 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
|
@ -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()
|
||||
{
|
||||
// TODO: RakClient::vftable_0() (saco 10034A30) (server L: 80691F0) (bot W: 403270 L: 806CBF2)
|
||||
}
|
||||
// Destructor
|
||||
RakClient::~RakClient()
|
||||
{}
|
||||
|
||||
void RakClient::vftable_4()
|
||||
#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_4() (saco 10034130) (server L: 8069200) (bot W: 402970 L: 806CC00)
|
||||
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
|
||||
}
|
||||
|
||||
unsigned i;
|
||||
|
||||
for ( i = 0; i < 32; i++ )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
if ( packet->data[ 0 ] == ID_REMOTE_DISCONNECTION_NOTIFICATION ||
|
||||
packet->data[ 0 ] == ID_REMOTE_CONNECTION_LOST )
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
else
|
||||
bitStream.IgnoreBits( sizeof( short ) * 8 );
|
||||
}
|
||||
}
|
||||
|
||||
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) )
|
||||
{
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
void RakClient::vftable_24()
|
||||
void RakClient::DeallocatePacket( Packet *packet )
|
||||
{
|
||||
// TODO: RakClient::vftable_24() (saco 10034A50) (server L: 8069280) (bot W: 403290 L: 806CC8A)
|
||||
RakPeer::DeallocatePacket( packet );
|
||||
}
|
||||
|
||||
void RakClient::vftable_28()
|
||||
void RakClient::PingServer( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_28() (saco 10034370) (server L: 8069290) (bot W: 402BB0 L: 806CCA4)
|
||||
if ( remoteSystemList == 0 )
|
||||
return ;
|
||||
|
||||
RakPeer::Ping( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_2C()
|
||||
void RakClient::PingServer( const char* host, unsigned short serverPort, unsigned short clientPort, bool onlyReplyOnAcceptingConnections )
|
||||
{
|
||||
// TODO: RakClient::vftable_2C() (saco 10034340) (server L: 80692A0) (bot W: 402B80 L: 806CCB2)
|
||||
RakPeer::Initialize( 1, clientPort, 0 );
|
||||
RakPeer::Ping( host, serverPort, onlyReplyOnAcceptingConnections );
|
||||
}
|
||||
|
||||
void RakClient::vftable_30()
|
||||
int RakClient::GetAveragePing( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_30() (saco 100343B0) (server L: 80692B0) (bot W: 402BF0 L: 806CCC0)
|
||||
if ( remoteSystemList == 0 )
|
||||
return -1;
|
||||
|
||||
return RakPeer::GetAveragePing( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_34()
|
||||
int RakClient::GetLastPing( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_34() (saco 100343E0) (server L: 80692C0) (bot W: 402C20 L: 806CCCE)
|
||||
if ( remoteSystemList == 0 )
|
||||
return -1;
|
||||
|
||||
return RakPeer::GetLastPing( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_38()
|
||||
int RakClient::GetLowestPing( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_38() (saco 10034410) (server L: 80692D0) (bot W: 402C50 L: 806CCDC)
|
||||
if ( remoteSystemList == 0 )
|
||||
return -1;
|
||||
|
||||
return RakPeer::GetLowestPing( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_3C()
|
||||
int RakClient::GetPlayerPing( const PlayerID playerId )
|
||||
{
|
||||
// TODO: RakClient::vftable_3C() (saco 10034440) (server L: 80692E0) (bot W: 402C80 L: 806CCEA)
|
||||
int i;
|
||||
|
||||
for ( i = 0; i < 32; i++ )
|
||||
if ( otherClients[ i ].playerId == playerId )
|
||||
return otherClients[ i ].ping;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RakClient::vftable_40()
|
||||
void RakClient::StartOccasionalPing( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_40() (saco 10034490) (server L: 80692F0) (bot W: 402CD0 L: 806CCF8)
|
||||
RakPeer::SetOccasionalPing( true );
|
||||
}
|
||||
|
||||
void RakClient::vftable_44()
|
||||
void RakClient::StopOccasionalPing( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_44() (saco 100344A0) (server L: 8069300) (bot W: 402CE0 L: 806CD06)
|
||||
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;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
i = GetOtherClientIndexByPlayerID( playerId );
|
||||
|
||||
if ( i >= 0 )
|
||||
{
|
||||
return & ( otherClients[ i ].staticData );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RakClient::vftable_90()
|
||||
void RakClient::SetStaticClientData( const PlayerID playerId, const char *data, const int length )
|
||||
{
|
||||
// TODO: RakClient::vftable_90() (saco 100350E0) (server L: 8069430) (bot W: 403920 L: 806CE10)
|
||||
int i;
|
||||
|
||||
if ( playerId == UNASSIGNED_PLAYER_ID )
|
||||
{
|
||||
localStaticData.Reset();
|
||||
localStaticData.Write( data, length );
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
i = GetOtherClientIndexByPlayerID( playerId );
|
||||
|
||||
if ( i >= 0 )
|
||||
{
|
||||
otherClients[ i ].staticData.Reset();
|
||||
otherClients[ i ].staticData.Write( data, length );
|
||||
}
|
||||
|
||||
else
|
||||
RakPeer::SetRemoteStaticData( playerId, data, length );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RakClient::vftable_94()
|
||||
void RakClient::SendStaticClientDataToServer( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_94() (saco 10035140) (server L: 8069440) (bot W: 403980 L: 806CE1E)
|
||||
if ( remoteSystemList == 0 )
|
||||
return ;
|
||||
|
||||
RakPeer::SendStaticData( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_98()
|
||||
PlayerID RakClient::GetServerID( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_98() (saco 10034760) (server L: 8069450) (bot W: 402FA0 L: 806CE2C)
|
||||
if ( remoteSystemList == 0 )
|
||||
return UNASSIGNED_PLAYER_ID;
|
||||
|
||||
return remoteSystemList[ 0 ].playerId;
|
||||
}
|
||||
|
||||
void RakClient::vftable_9C()
|
||||
PlayerID RakClient::GetPlayerID( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_9C() (saco 10034790) (server L: 8069460) (bot W: 402FD0 L: 806CE3A)
|
||||
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_A0()
|
||||
PlayerID RakClient::GetInternalID( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_A0() (saco 100347D0) (server L: 8069470) (bot W: 403010 L: 806CE48)
|
||||
return RakPeer::GetInternalID();
|
||||
}
|
||||
|
||||
void RakClient::vftable_A4()
|
||||
const char* RakClient::PlayerIDToDottedIP( const PlayerID playerId ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_A4() (saco 10034AF0) (server L: 8069480) (bot W: 403330 L: 806CE56)
|
||||
return RakPeer::PlayerIDToDottedIP( playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_A8()
|
||||
void RakClient::PushBackPacket( Packet *packet, bool pushAtHead )
|
||||
{
|
||||
// TODO: RakClient::vftable_A8() (saco 10034B00) (server L: 8069490) (bot W: 403340 L: 806CE64)
|
||||
RakPeer::PushBackPacket(packet, pushAtHead);
|
||||
}
|
||||
|
||||
void RakClient::vftable_AC()
|
||||
void RakClient::SetRouterInterface( RouterInterface *routerInterface )
|
||||
{
|
||||
// TODO: RakClient::vftable_AC() (saco 10034B10) (server L: 80694A0) (bot W: 403350 L: 806CE72)
|
||||
RakPeer::SetRouterInterface(routerInterface);
|
||||
}
|
||||
void RakClient::RemoveRouterInterface( RouterInterface *routerInterface )
|
||||
{
|
||||
RakPeer::RemoveRouterInterface(routerInterface);
|
||||
}
|
||||
|
||||
void RakClient::vftable_B0()
|
||||
void RakClient::SetTimeoutTime( RakNetTime timeMS )
|
||||
{
|
||||
// TODO: RakClient::vftable_B0() (saco 10034B20) (server L: 80694B0) (bot W: 403360 L: 806CE80)
|
||||
RakPeer::SetTimeoutTime( timeMS, GetServerID() );
|
||||
}
|
||||
|
||||
void RakClient::vftable_B4()
|
||||
bool RakClient::SetMTUSize( int size )
|
||||
{
|
||||
// TODO: RakClient::vftable_B4() (saco 10034B30) (server L: 80694C0) (bot W: 403370 L: 806CE8E)
|
||||
return RakPeer::SetMTUSize( size );
|
||||
}
|
||||
|
||||
void RakClient::vftable_B8()
|
||||
int RakClient::GetMTUSize( void ) const
|
||||
{
|
||||
// TODO: RakClient::vftable_B8() (saco 100348E0) (server L: 80694D0) (bot W: 403120 L: 806CE9C)
|
||||
return RakPeer::GetMTUSize();
|
||||
}
|
||||
|
||||
void RakClient::vftable_BC()
|
||||
void RakClient::AllowConnectionResponseIPMigration( bool allow )
|
||||
{
|
||||
// TODO: RakClient::vftable_BC() (saco 10034B40) (server L: 80694E0) (bot W: 403380 L: 806CEAA)
|
||||
RakPeer::AllowConnectionResponseIPMigration( allow );
|
||||
}
|
||||
|
||||
void RakClient::vftable_C0()
|
||||
void RakClient::AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength )
|
||||
{
|
||||
// TODO: RakClient::vftable_C0() (saco 10034B50) (server L: 80694F0) (bot W: 403390 L: 806CEB8)
|
||||
RakPeer::AdvertiseSystem( host, remotePort, data, dataLength );
|
||||
}
|
||||
|
||||
void RakClient::vftable_C4()
|
||||
RakNetStatisticsStruct* const RakClient::GetStatistics( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_C4() (saco 10034B60) (server L: 8069500) (bot W: 4033A0 L: 806CEC6)
|
||||
return RakPeer::GetStatistics( remoteSystemList[ 0 ].playerId );
|
||||
}
|
||||
|
||||
void RakClient::vftable_C8()
|
||||
void RakClient::ApplyNetworkSimulator( double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance)
|
||||
{
|
||||
// TODO: RakClient::vftable_C8() (saco 10034B70) (server L: 8069510) (bot W: 4033B0 L: 806CED4)
|
||||
RakPeer::ApplyNetworkSimulator( maxSendBPS, minExtraPing, extraPingVariance );
|
||||
}
|
||||
|
||||
void RakClient::vftable_CC()
|
||||
bool RakClient::IsNetworkSimulatorActive( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_CC() (saco 10034960) (server L: 8069520) (bot W: 4031A0 L: 806CEE2)
|
||||
return RakPeer::IsNetworkSimulatorActive();
|
||||
}
|
||||
|
||||
void RakClient::vftable_D0()
|
||||
int RakClient::GetOtherClientIndexByPlayerID( const PlayerID playerId )
|
||||
{
|
||||
// TODO: RakClient::vftable_D0() (saco 10034B80) (server L: 8069530) (bot W: 4033C0 L: 806CEF0)
|
||||
unsigned i;
|
||||
|
||||
for ( i = 0; i < 32; i++ )
|
||||
{
|
||||
if ( otherClients[ i ].playerId == playerId )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RakClient::vftable_D4()
|
||||
int RakClient::GetFreeOtherClientIndex( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_D4() (saco 10034B90) (server L: 8069540) (bot W: 4033D0 L: 806CEFE)
|
||||
unsigned i;
|
||||
|
||||
for ( i = 0; i < 32; i++ )
|
||||
{
|
||||
if ( otherClients[ i ].isActive == false )
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RakClient::vftable_D8()
|
||||
PlayerIndex RakClient::GetPlayerIndex( void )
|
||||
{
|
||||
// TODO: RakClient::vftable_D8() (saco 10034A20) (server L: 8069550) (bot W: 403260 L: 806CF0C)
|
||||
}
|
||||
|
||||
void RakClient::vftable_DC()
|
||||
{
|
||||
// TODO: RakClient::vftable_DC() (bot L: 0806CF1A)
|
||||
return localPlayerIndex;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user