diff --git a/Multiplayer/raknet/AsynchronousFileIO.h b/Multiplayer/raknet/AsynchronousFileIO.h new file mode 100644 index 000000000..e2de91897 --- /dev/null +++ b/Multiplayer/raknet/AsynchronousFileIO.h @@ -0,0 +1,91 @@ +/// \file +/// \brief \b [Internal] Depreciated, used for windows back when I supported IO completion ports. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +// No longer used as I no longer support IO Completion ports +/* +#ifdef __USE_IO_COMPLETION_PORTS + +#ifndef __ASYNCHRONOUS_FILE_IO_H +#define __ASYNCHRONOUS_FILE_IO_H + +#ifdef _XBOX +#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 +//#include +#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 +*/ \ No newline at end of file diff --git a/Multiplayer/raknet/AutoRPC.h b/Multiplayer/raknet/AutoRPC.h new file mode 100644 index 000000000..9dc480e35 --- /dev/null +++ b/Multiplayer/raknet/AutoRPC.h @@ -0,0 +1,651 @@ +/// \file +/// \brief Automatically serializing and deserializing RPC system. More advanced RPC, but possibly not cross-platform +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __AUTO_RPC_H +#define __AUTO_RPC_H + +class RakPeerInterface; +class NetworkIDManager; +#include "PluginInterface.h" +#include "DS_Map.h" +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "BitStream.h" +#include "Gen_RPC8.h" +#include "RakString.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// \defgroup AUTO_RPC_GROUP AutoRPC +/// \ingroup PLUGINS_GROUP + +namespace RakNet +{ + +/// Maximum amount of data that can be passed on the stack in a function call +#define ARPC_MAX_STACK_SIZE 65536 + +#if defined (_WIN32) +/// Easier way to get a pointer to a function member of a C++ class +/// \note Recommended you use ARPC_REGISTER_CPP_FUNCTION0 to ARPC_REGISTER_CPP_FUNCTION9 (below) +/// \note ARPC_REGISTER_CPP_FUNCTION is not Linux compatible, and cannot validate the number of parameters is correctly passed. +/// \param[in] autoRPCInstance A pointer to an instance of AutoRPC +/// \param[in] _IDENTIFIER_ C string identifier to use on the remote system to call the function +/// \param[in] _RETURN_ Return value of the function +/// \param[in] _CLASS_ Base-most class of the containing class that contains your function +/// \param[in] _FUNCTION_ Name of the function +/// \param[in] _PARAMS_ Parameter list, include parenthesis +#define ARPC_REGISTER_CPP_FUNCTION(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS_) \ +{ \ +union \ +{ \ + _RETURN_ (AUTO_RPC_CALLSPEC _CLASS_::*__memberFunctionPtr)_PARAMS_; \ + void* __voidFunc; \ +}; \ + __memberFunctionPtr=&_CLASS_::_FUNCTION_; \ + (autoRPCInstance)->RegisterFunction(_IDENTIFIER_, __voidFunc, true, -1); \ +} + +/// \internal Used by ARPC_REGISTER_CPP_FUNCTION0 to ARPC_REGISTER_CPP_FUNCTION9 +#define ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS_, _PARAM_COUNT_) \ + { \ + union \ + { \ + _RETURN_ (AUTO_RPC_CALLSPEC _CLASS_::*__memberFunctionPtr)_PARAMS_; \ + void* __voidFunc; \ +}; \ + __memberFunctionPtr=&_CLASS_::_FUNCTION_; \ + (autoRPCInstance)->RegisterFunction(_IDENTIFIER_, __voidFunc, true, _PARAM_COUNT_); \ +} + +/// Same as ARPC_REGISTER_CPP_FUNCTION, but specifies how many parameters the function has +#define ARPC_REGISTER_CPP_FUNCTION0(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_) (autoRPCInstance)->RegisterFunction(_IDENTIFIER_, __voidFunc, true, 0); +#define ARPC_REGISTER_CPP_FUNCTION1(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_), 0) +#define ARPC_REGISTER_CPP_FUNCTION2(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_), 1) +#define ARPC_REGISTER_CPP_FUNCTION3(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_), 2) +#define ARPC_REGISTER_CPP_FUNCTION4(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_), 3) +#define ARPC_REGISTER_CPP_FUNCTION5(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_,_PARAMS5_), 4) +#define ARPC_REGISTER_CPP_FUNCTION6(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_,_PARAMS5_,_PARAMS6_), 5) +#define ARPC_REGISTER_CPP_FUNCTION7(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_,_PARAMS5_,_PARAMS6_,_PARAMS7_), 6) +#define ARPC_REGISTER_CPP_FUNCTION8(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_,_PARAMS5_,_PARAMS6_,_PARAMS7_,_PARAMS8_), 7) +#define ARPC_REGISTER_CPP_FUNCTION9(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_, _PARAMS9_) ARPC_REGISTER_CPP_FUNCTIONX(autoRPCInstance,_IDENTIFIER_,_RETURN_,_CLASS_,_FUNCTION_,(_PARAMS1_,_PARAMS2_,_PARAMS3_,_PARAMS4_,_PARAMS5_,_PARAMS6_,_PARAMS7_,_PARAMS8_,_PARAMS9_), 8) + +#else + +#define ARPC_REGISTER_CPP_FUNCTION0(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*)) &_CLASS_::_FUNCTION_, true, 0 ); + +#define ARPC_REGISTER_CPP_FUNCTION1(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_ )) &_CLASS_::_FUNCTION_, true, 0 ); + +#define ARPC_REGISTER_CPP_FUNCTION2(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_ )) &_CLASS_::_FUNCTION_, true, 1 ); + +#define ARPC_REGISTER_CPP_FUNCTION3(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_ )) &_CLASS_::_FUNCTION_, true, 2 ); + +#define ARPC_REGISTER_CPP_FUNCTION4(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_ )) &_CLASS_::_FUNCTION_, true, 3 ); + +#define ARPC_REGISTER_CPP_FUNCTION5(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_ )) &_CLASS_::_FUNCTION_, true, 4 ); + +#define ARPC_REGISTER_CPP_FUNCTION6(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_ )) &_CLASS_::_FUNCTION_, true, 5 ); + +#define ARPC_REGISTER_CPP_FUNCTION7(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_ )) &_CLASS_::_FUNCTION_, true, 6 ); + +#define ARPC_REGISTER_CPP_FUNCTION8(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_ )) &_CLASS_::_FUNCTION_, true, 7 ); + +#define ARPC_REGISTER_CPP_FUNCTION9(autoRPCInstance, _IDENTIFIER_, _RETURN_, _CLASS_, _FUNCTION_, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_, _PARAMS9_) \ + (autoRPCInstance)->RegisterFunction((_IDENTIFIER_), (void*)(_RETURN_ (*) (_CLASS_*, _PARAMS1_, _PARAMS2_, _PARAMS3_, _PARAMS4_, _PARAMS5_, _PARAMS6_, _PARAMS7_, _PARAMS8_, _PARAMS9_ )) &_CLASS_::_FUNCTION_, true, 8 ); + +#endif + +/// Error codes returned by a remote system as to why an RPC function call cannot execute +/// Follows packet ID ID_RPC_REMOTE_ERROR +/// Name of the function will be appended, if available. Read as follows: +/// char outputBuff[256]; +/// stringCompressor->DecodeString(outputBuff,256,&RakNet::BitStream(p->data+sizeof(MessageID)+1,p->length-sizeof(MessageID)-1,false),0); +/// printf("Function: %s\n", outputBuff); +enum RPCErrorCodes +{ + /// AutoRPC::SetNetworkIDManager() was not called, and it must be called to call a C++ object member + RPC_ERROR_NETWORK_ID_MANAGER_UNAVAILABLE, + + /// Cannot execute C++ object member call because the object specified by SetRecipientObject() does not exist on this system + RPC_ERROR_OBJECT_DOES_NOT_EXIST, + + /// Internal error, index optimization for function lookup does not exist + RPC_ERROR_FUNCTION_INDEX_OUT_OF_RANGE, + + /// Named function was not registered with RegisterFunction(). Check your spelling. + RPC_ERROR_FUNCTION_NOT_REGISTERED, + + /// Named function was registered, but later unregistered with UnregisterFunction() and can no longer be called. + RPC_ERROR_FUNCTION_NO_LONGER_REGISTERED, + + /// SetRecipientObject() was not called before Call(), but RegisterFunction() was called with isObjectMember=true + /// If you intended to call a CPP function, call SetRecipientObject() with a valid object first. + RPC_ERROR_CALLING_CPP_AS_C, + + /// SetRecipientObject() was called before Call(), but RegisterFunction() was called with isObjectMember=false + /// If you intended to call a C function, call SetRecipientObject(UNASSIGNED_NETWORK_ID) first. + RPC_ERROR_CALLING_C_AS_CPP, + + /// Internal error, passed stack is bigger than current stack. Check that the version is the same on both systems. + RPC_ERROR_STACK_TOO_SMALL, + + /// Internal error, formatting error with how the stack was serialized + RPC_ERROR_STACK_DESERIALIZATION_FAILED, + + /// The \a parameterCount parameter passed to RegisterFunction() on this system does not match the \a parameterCount parameter passed to SendCall() on the remote system. + RPC_ERROR_INCORRECT_NUMBER_OF_PARAMETERS, +}; + +/// The AutoRPC plugin allows you to call remote functions as if they were local functions, using the standard function call syntax +/// No serialization or deserialization is needed. +/// Advantages are that this is easier to use than regular RPC system. +/// Disadvantages is that all parameters must be passable on the stack using memcpy (shallow copy). For other types of parameters, use SetOutgoingExtraData() and GetIncomingExtraData() +/// Pointers are automatically dereferenced and the contents copied with memcpy +/// Use the old system, or regular message passing, if you need greater flexibility +/// \ingroup AUTO_RPC_GROUP +class AutoRPC : public PluginInterface +{ +public: + /// Constructor + AutoRPC(); + + /// Destructor + virtual ~AutoRPC(); + + /// Sets the network ID manager to use for object lookup + /// Required to call C++ object member functions via SetRecipientObject() + /// \param[in] idMan Pointer to the network ID manager to use + void SetNetworkIDManager(NetworkIDManager *idMan); + + /// Registers a function pointer to be callable given an identifier for the pointer + /// \param[in] uniqueIdentifier String identifying the function. Recommended that this is the name of the function + /// \param[in] functionPtr Pointer to the function. For C, just pass the name of the function. For C++, use ARPC_REGISTER_CPP_FUNCTION + /// \param[in] isObjectMember false if a C function. True if a member function of an object (C++) + /// \param[in] parameterCount Optional parameter to tell the system how many parameters this function has. If specified, and the wrong number of parameters are called by the remote system, the call is rejected. -1 indicates undefined + /// \return True on success, false on uniqueIdentifier already used + bool RegisterFunction(const char *uniqueIdentifier, void *functionPtr, bool isObjectMember, char parameterCount=-1); + + /// Unregisters a function pointer to be callable given an identifier for the pointer + /// \note This is not safe to call while connected + /// \param[in] uniqueIdentifier String identifying the function. + /// \param[in] isObjectMember false if a C function. True if a member function of an object (C++) + /// \return True on success, false on function was not previously or is not currently registered. + bool UnregisterFunction(const char *uniqueIdentifier, bool isObjectMember); + + /// Send or stop sending a timestamp with all following calls to Call() + /// Use GetLastSenderTimestamp() to read the timestamp. + /// \param[in] timeStamp Non-zero to pass this timestamp using the ID_TIMESTAMP system. 0 to clear passing a timestamp. + void SetTimestamp(RakNetTime timeStamp); + + /// Set parameters to pass to RakPeer::Send() for all following calls to Call() + /// Deafults to HIGH_PRIORITY, RELIABLE_ORDERED, ordering channel 0 + /// \param[in] priority See RakPeer::Send() + /// \param[in] reliability See RakPeer::Send() + /// \param[in] orderingChannel See RakPeer::Send() + void SetSendParams(PacketPriority priority, PacketReliability reliability, char orderingChannel); + + /// Set system to send to for all following calls to Call() + /// Defaults to UNASSIGNED_SYSTEM_ADDRESS, broadcast=true + /// \param[in] systemAddress See RakPeer::Send() + /// \param[in] broadcast See RakPeer::Send() + void SetRecipientAddress(SystemAddress systemAddress, bool broadcast); + + /// Set the NetworkID to pass for all following calls to Call() + /// Defaults to UNASSIGNED_NETWORK_ID (none) + /// If set, the remote function will be considered a C++ function, e.g. an object member function + /// If set to UNASSIGNED_NETWORK_ID (none), the remote function will be considered a C function + /// If this is set incorrectly, you will get back either RPC_ERROR_CALLING_C_AS_CPP or RPC_ERROR_CALLING_CPP_AS_C + /// \sa NetworkIDManager + /// \param[in] networkID Returned from NetworkIDObject::GetNetworkID() + void SetRecipientObject(NetworkID networkID); + + /// Write extra data to pass for all following calls to Call() + /// Use BitStream::Reset to clear extra data. Don't forget to do this or you will waste bandwidth. + /// \return A bitstream you can write to to send extra data with each following call to Call() + RakNet::BitStream *SetOutgoingExtraData(void); + + /// If the last received function call has a timestamp included, it is stored and can be retrieved with this function. + /// \return 0 if the last call did not have a timestamp, else non-zero + RakNetTime GetLastSenderTimestamp(void) const; + + /// Returns the system address of the last system to send us a received function call + /// Equivalent to the old system RPCParameters::sender + /// \return Last system to send an RPC call using this system + SystemAddress GetLastSenderAddress(void) const; + + /// Returns the instance of RakPeer this plugin was attached to + RakPeerInterface *GetRakPeer(void) const; + + /// Returns the currently running RPC call identifier, set from RegisterFunction::uniqueIdentifier + /// Returns an empty string "" if none + /// \return which RPC call is currently running + const char *GetCurrentExecution(void) const; + + /// Gets the bitstream written to via SetOutgoingExtraData(). + /// Data is updated with each incoming function call + /// \return A bitstream you can read from with extra data that was written with SetOutgoingExtraData(); + RakNet::BitStream *GetIncomingExtraData(void); + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + bool Call(const char *uniqueIdentifier){ + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 0); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 1); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 2); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 3); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 4); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 5); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 6); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, true, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 7); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + template + bool Call(const char *uniqueIdentifier, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 ) { + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, p8, true, true, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 8); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID){ + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 0); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 1); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 2); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 3); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 4); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 5); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 6); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, true, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 7); + } + + /// Calls a remote function, using whatever was last passed to SetTimestamp(), SetSendParams(), SetRecipientAddress(), and SetRecipientObject() + /// Passed parameter(s), if any, are passed via memcpy and pushed on the stack for the remote function + /// \note This ONLY works with variables that are passable via memcpy! If you need more flexibility, use SetOutgoingExtraData() and GetIncomingExtraData() + /// \note The this pointer, for this instance of AutoRPC, is pushed as the last parameter on the stack. See AutoRPCSample.ccp for an example of this + /// \param[in] uniqueIdentifier parameter of the same name passed to RegisterFunction() on the remote system + /// \param[in] timeStamp See SetTimestamp() + /// \param[in] priority See SetSendParams() + /// \param[in] reliability See SetSendParams() + /// \param[in] orderingChannel See SetSendParams() + /// \param[in] systemAddress See SetRecipientAddress() + /// \param[in] broadcast See SetRecipientAddress() + /// \param[in] networkID See SetRecipientObject() + template + bool CallExplicit(const char *uniqueIdentifier, RakNetTime timeStamp, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, NetworkID networkID, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8 ) { + SetTimestamp(timeStamp); + SetSendParams(priority, reliability, orderingChannel); + SetRecipientAddress(systemAddress, broadcast); + SetRecipientObject(networkID); + char stack[ARPC_MAX_STACK_SIZE]; + unsigned int bytesOnStack = GenRPC::BuildStack(stack, p1, p2, p3, p4, p5, p6, p7, p8, true, true, true, true, true, true, true, true); + return SendCall(uniqueIdentifier, stack, bytesOnStack, 8); + } + + + // If you need more than 8 parameters, just add it here... + + // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- + + /// \internal + /// Identifies an RPC function, by string identifier and if it is a C or C++ function + struct RPCIdentifier + { + char *uniqueIdentifier; + bool isObjectMember; + }; + + /// \internal + /// The RPC identifier, and a pointer to the function + struct LocalRPCFunction + { + RPCIdentifier identifier; + void *functionPtr; + char parameterCount; + }; + + /// \internal + /// The RPC identifier, and the index of the function on a remote system + struct RemoteRPCFunction + { + RPCIdentifier identifier; + unsigned int functionIndex; + }; + + /// \internal + static int RemoteRPCFunctionComp( const RPCIdentifier &key, const RemoteRPCFunction &data ); + + /// \internal + /// Sends the RPC call, with a given serialized stack + bool SendCall(const char *uniqueIdentifier, const char *stack, unsigned int bytesOnStack, char parameterCount); + +protected: + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + void OnAttach(RakPeerInterface *peer); + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + virtual void OnAutoRPCCall(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes); + virtual void OnRPCRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes); + virtual void OnRPCUnknownRemoteIndex(SystemAddress systemAddress, unsigned char *data, unsigned int lengthInBytes, RakNetTime timestamp); + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + virtual void OnShutdown(RakPeerInterface *peer); + + void Clear(void); + + void SendError(SystemAddress target, unsigned char errorCode, const char *functionName); + unsigned GetLocalFunctionIndex(RPCIdentifier identifier); + bool GetRemoteFunctionIndex(SystemAddress systemAddress, RPCIdentifier identifier, unsigned int *outerIndex, unsigned int *innerIndex); + + + DataStructures::List localFunctions; + DataStructures::Map *> remoteFunctions; + + RakNetTime outgoingTimestamp; + PacketPriority outgoingPriority; + PacketReliability outgoingReliability; + char outgoingOrderingChannel; + SystemAddress outgoingSystemAddress; + bool outgoingBroadcast; + NetworkID outgoingNetworkID; + RakNet::BitStream outgoingExtraData; + + RakNetTime incomingTimeStamp; + SystemAddress incomingSystemAddress; + RakNet::BitStream incomingExtraData; + + RakPeerInterface *rakPeer; + NetworkIDManager *networkIdManager; + char currentExecution[512]; +}; + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/Multiplayer/raknet/AutopatcherPatchContext.h b/Multiplayer/raknet/AutopatcherPatchContext.h new file mode 100644 index 000000000..3bd4b280e --- /dev/null +++ b/Multiplayer/raknet/AutopatcherPatchContext.h @@ -0,0 +1,17 @@ +#ifndef __AUTOPATCHER_PATCH_CONTEXT_H +#define __AUTOPATCHER_PATCH_CONTEXT_H + +enum PatchContext +{ + PC_HASH_WITH_PATCH, + PC_WRITE_FILE, + PC_ERROR_FILE_WRITE_FAILURE, + PC_ERROR_PATCH_TARGET_MISSING, + PC_ERROR_PATCH_APPLICATION_FAILURE, + PC_ERROR_PATCH_RESULT_CHECKSUM_FAILURE, + PC_NOTICE_WILL_COPY_ON_RESTART, + PC_NOTICE_FILE_DOWNLOADED, + PC_NOTICE_FILE_DOWNLOADED_PATCH, +}; + +#endif diff --git a/Multiplayer/raknet/AutopatcherRepositoryInterface.h b/Multiplayer/raknet/AutopatcherRepositoryInterface.h new file mode 100644 index 000000000..e46f23a90 --- /dev/null +++ b/Multiplayer/raknet/AutopatcherRepositoryInterface.h @@ -0,0 +1,55 @@ +/// \file +/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + + +#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H +#define __AUTOPATCHER_REPOSITORY_INTERFACE_H + +class FileList; +namespace RakNet +{ + class BitStream; +} + +#include "IncrementalReadInterface.h" + +/// 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 IncrementalReadInterface +{ +public: + /// Get list of files added and deleted since a certain date. This is used by AutopatcherServer and not usually explicitly called. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[out] addedFiles A list of the current versions of filenames with hashes as their data that were created after \a sinceData + /// \param[out] deletedFiles A list of the current versions of filenames that were deleted after \a sinceData + /// \param[in] An input date, in whatever format your repository uses + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetChangelistSinceDate(const char *applicationName, FileList *addedFiles, FileList *deletedFiles, const char *sinceDate, char currentDate[64])=0; + + /// Get patches (or files) for every file in input, assuming that input has a hash for each of those files. + /// \param[in] applicationName A null terminated string identifying the application + /// \param[in] input A list of files with SHA1_LENGTH byte hashes to get from the database. + /// \param[out] patchList You should return list of files with either the filedata or the patch. This is a subset of \a input. The context data for each file will be either PC_WRITE_FILE (to just write the file) or PC_HASH_WITH_PATCH (to patch). If PC_HASH_WITH_PATCH, then the file contains a SHA1_LENGTH byte patch followed by the hash. The datalength is patchlength + SHA1_LENGTH + /// \param[out] currentDate The current server date, in whatever format your repository uses + /// \return True on success, false on failure. + virtual bool GetPatches(const char *applicationName, FileList *input, FileList *patchList, char currentDate[64])=0; + + /// \return Whatever this function returns is sent from the AutopatcherServer to the AutopatcherClient when one of the above functions returns false. + virtual const char *GetLastError(void) const=0; +}; + +#endif diff --git a/Multiplayer/raknet/BigInt.h b/Multiplayer/raknet/BigInt.h new file mode 100644 index 000000000..881f7c708 --- /dev/null +++ b/Multiplayer/raknet/BigInt.h @@ -0,0 +1,353 @@ +/* + * BigInts are stored as 32-bit integer arrays. + * Each integer in the array is referred to as a limb ala GMP. + * Lower numbered limbs are less significant to the number represented. + * eg, limb 0 is the least significant limb. + * Also known as little-endian digit order + */ + +#ifndef BIG_INT_HPP +#define BIG_INT_HPP + +#include "Platform.h" +//#include + +namespace big +{ + // returns the degree of the base 2 monic polynomial + // (the number of bits used to represent the number) + // eg, 0 0 0 0 1 0 1 1 ... => 28 out of 32 used + uint32_t Degree(uint32_t v); + + // returns the number of limbs that are actually used + int LimbDegree(const uint32_t *n, int limbs); + + // return bits used + uint32_t Degree(const uint32_t *n, int limbs); + + // lhs = rhs (unequal limbs) + void Set(uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs = rhs (equal limbs) + void Set(uint32_t *lhs, int limbs, const uint32_t *rhs); + + // lhs = rhs (32-bit extension) + void Set32(uint32_t *lhs, int lhs_limbs, const uint32_t rhs); + + // Comparisons where both operands have the same number of limbs + bool Less(int limbs, const uint32_t *lhs, const uint32_t *rhs); + bool Greater(int limbs, const uint32_t *lhs, const uint32_t *rhs); + bool Equal(int limbs, const uint32_t *lhs, const uint32_t *rhs); + + // lhs < rhs + bool Less(const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs >= rhs + inline bool GreaterOrEqual(const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs) + { + return !Less(lhs, lhs_limbs, rhs, rhs_limbs); + } + + // lhs > rhs + bool Greater(const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs <= rhs + inline bool LessOrEqual(const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs) + { + return !Greater(lhs, lhs_limbs, rhs, rhs_limbs); + } + + // lhs == rhs + bool Equal(const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs == rhs + bool Equal32(const uint32_t *lhs, int lhs_limbs, uint32_t rhs); + + // n >>= shift*32 + void LimbShiftRight(uint32_t *n, int limbs, int rhs_shift); + + // n <<= shift*32 + void LimbShiftLeft(uint32_t *n, int limbs, int rhs_shift); + + // lhs = rhs >>> shift + // Precondition: 0 <= shift < 31 + void BitShiftRight(uint32_t *result, int result_limbs, const uint32_t *lhs, int lhs_limbs, int rhs_shift); + + // lhs = rhs <<< shift + // Precondition: 0 <= shift < 31 + void BitShiftLeft(uint32_t *result, int result_limbs, const uint32_t *lhs, int lhs_limbs, int rhs_shift); + + // lhs += rhs, return carry out + // precondition: lhs_limbs >= rhs_limbs + uint32_t Add(uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // out = lhs + rhs, return carry out + // precondition: lhs_limbs >= rhs_limbs + uint32_t Add(uint32_t *out, const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs += rhs, return carry out + // precondition: lhs_limbs > 0 + uint32_t Add32(uint32_t *lhs, int lhs_limbs, uint32_t rhs); + + // lhs -= rhs, return borrow out + // precondition: lhs_limbs >= rhs_limbs + int32_t Subtract(uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // out = lhs - rhs, return borrow out + // precondition: lhs_limbs >= rhs_limbs + int32_t Subtract(uint32_t *out, const uint32_t *lhs, int lhs_limbs, const uint32_t *rhs, int rhs_limbs); + + // lhs -= rhs, return borrow out + // precondition: lhs_limbs > 0, result limbs = lhs_limbs + int32_t Subtract32(uint32_t *lhs, int lhs_limbs, uint32_t rhs); + + // n = -n + void Negate(uint32_t *n, int limbs); + + // n = ~n, only invert bits up to the MSB, but none above that + void BitNot(uint32_t *n, int limbs); + + // n = ~n, invert all bits, even ones above MSB + void LimbNot(uint32_t *n, int limbs); + + // Return the carry out from A += B << S + uint32_t AddLeftShift32( + int limbs, // Number of limbs in parameter A and B + uint32_t *A, // Large number + const uint32_t *B, // Large number + uint32_t S); // 32-bit number + + // Return the carry out from result = A * B + uint32_t Multiply32( + int limbs, // Number of limbs in parameter A, result + uint32_t *result, // Large number + const uint32_t *A, // Large number + uint32_t B); // 32-bit number + + // Return the carry out from X = X * M + A + uint32_t MultiplyAdd32( + int limbs, // Number of limbs in parameter A and B + uint32_t *X, // Large number + uint32_t M, // Large number + uint32_t A); // 32-bit number + + // Return the carry out from A += B * M + uint32_t AddMultiply32( + int limbs, // Number of limbs in parameter A and B + uint32_t *A, // Large number + const uint32_t *B, // Large number + uint32_t M); // 32-bit number + + // product = x * y + void SimpleMultiply( + int limbs, // Number of limbs in parameters x, y + uint32_t *product, // Large number; buffer size = limbs*2 + const uint32_t *x, // Large number + const uint32_t *y); // Large number + + // product = x ^ 2 + void SimpleSquare( + int limbs, // Number of limbs in parameter x + uint32_t *product, // Large number; buffer size = limbs*2 + const uint32_t *x); // Large number + + /* + * Multiply two large numbers using the Schoolbook method + * Only produces the low y_limbs of the result + * + * The product buffer may not be pointed to by x or y + */ + void HalfSchoolbookMultiply( + uint32_t *product, // Buffer size = x_limbs+y_limbs + const uint32_t *x, // Number to multiply, buffer size = x_limbs + int x_limbs, // Size of x + const uint32_t *y, // Number to multiply, buffer size = y_limbs + int y_limbs); // Size of y + + /* + * Multiply two large numbers using the Schoolbook method + * + * The product buffer may not be pointed to by x or y + */ + void SchoolbookMultiply( + uint32_t *product, // Buffer size = x_limbs+y_limbs + const uint32_t *x, // Number to multiply, buffer size = x_limbs + int x_limbs, // Size of x + const uint32_t *y, // Number to multiply, buffer size = y_limbs + int y_limbs); // Size of y + + // product = xy + // memory space for product may not overlap with x,y + void Multiply( + int limbs, // Number of limbs in x,y + uint32_t *product, // Product; buffer size = limbs*2 + const uint32_t *x, // Large number; buffer size = limbs + const uint32_t *y); // Large number; buffer size = limbs + + // product = low half of x * y product + void SimpleMultiplyLowHalf( + int limbs, // Number of limbs in parameters x, y + uint32_t *product, // Large number; buffer size = limbs + const uint32_t *x, // Large number + const uint32_t *y); // Large number + + // product = x^2 + // memory space for product may not overlap with x + void Square( + int limbs, // Number of limbs in x + uint32_t *product, // Product; buffer size = limbs*2 + const uint32_t *x); // Large number; buffer size = limbs + + // Multiply two large, 2's complement signed numbers: result = a0 * b0 + void SignedMultiply( + int limbs, // Number of limbs in parameters a0,b0 + uint32_t *result, // Output, buffer size = limbs*2 + const uint32_t *a0, // Large number, buffer size = limbs + const uint32_t *b0); // Large number, buffer size = limbs + + // Returns the remainder of N / divisor for a 32-bit divisor + uint32_t Modulus32( + int limbs, // Number of limbs in parameter N + const uint32_t *N, // Large number, buffer size = limbs + uint32_t divisor); // 32-bit number + + /* + * 'A' is overwritten with the quotient of the operation + * Returns the remainder of 'A' / divisor for a 32-bit divisor + * + * Does not check for divide-by-zero + */ + uint32_t Divide32( + int limbs, // Number of limbs in parameter A + uint32_t *A, // Large number, buffer size = limbs + uint32_t divisor); // 32-bit number + + // returns (n ^ -1) Mod 2^32 + uint32_t MulInverseGF32(uint32_t n); + + /* + * Schoolbook division algorithm + * + * Returns true on success and false on failure (like divide by 0) + * + * Quotient and Remainder pointers can be the same as any other + */ + bool SchoolbookDivide( + const uint32_t *dividend, // Large number (numerator), buffer size = dividend_limbs + int dividend_limbs, // Dividend limbs + const uint32_t *divisor, // Large number (denominator), buffer size = divisor_limbs + int divisor_limbs, // Divisor limbs + uint32_t *quotient, // Quotient of division, buffer size = dividend_limbs + uint32_t *remainder); // Remainder of division, buffer size = divisor_limbs + + // Convert bigint to string + //std::string ToStr(const uint32_t *n, int limbs, int base = 10); + + // Convert string to bigint + // Return 0 if string contains non-digit characters, else number of limbs used + int ToInt(uint32_t *lhs, int max_limbs, const char *rhs, uint32_t base = 10); + + /* + * Computes: result = (n ^ -1) (Mod modulus) + * Such that: result * n (Mod modulus) = 1 + * Using Extended Euclid's Algorithm (GCDe) + * + * This is not always possible, so it will return false iff not possible. + */ + bool InvMod( + const uint32_t *n, // Large number, buffer size = n_limbs + int n_limbs, // Size of n + const uint32_t *modulus, // Large number, buffer size = limbs + int limbs, // Size of modulus + uint32_t *result); // Large number, buffer size = limbs + + /* + * Computes: result = GCD(a, b) (greatest common divisor) + * + * Length of result is the length of the smallest argument + */ + void GCD( + const uint32_t *a, // Large number, buffer size = a_limbs + int a_limbs, // Size of a + const uint32_t *b, // Large number, buffer size = b_limbs + int b_limbs, // Size of b + uint32_t *result); // Large number, buffer size = min(a, b) + + // Calculates mod_inv from low limb of modulus + uint32_t MonModInv(uint32_t modulus0); + + // Compute n_residue for Montgomery reduction + void MonInputResidue( + const uint32_t *n, // Large number, buffer size = n_limbs + int n_limbs, // Number of limbs in n + const uint32_t *modulus, // Large number, buffer size = m_limbs + int m_limbs, // Number of limbs in modulus + uint32_t *n_residue); // Result, buffer size = m_limbs + + // result = a * b * r^-1 (Mod modulus) in Montgomery domain + void MonPro( + int limbs, // Number of limbs in each parameter + const uint32_t *a_residue, // Large number, buffer size = limbs + const uint32_t *b_residue, // Large number, buffer size = limbs + const uint32_t *modulus, // Large number, buffer size = limbs + uint32_t mod_inv, // MonModInv() return + uint32_t *result); // Large number, buffer size = limbs + + // result = a * r^-1 (Mod modulus) in Montgomery domain + // The result may be greater than the modulus, but this is okay since + // the result is still in the RNS. MonFinish() corrects this at the end. + void MonReduce( + int limbs, // Number of limbs in each parameter + uint32_t *s, // Large number, buffer size = limbs*2, gets clobbered + const uint32_t *modulus, // Large number, buffer size = limbs + uint32_t mod_inv, // MonModInv() return + uint32_t *result); // Large number, buffer size = limbs + + // result = a * r^-1 (Mod modulus) in Montgomery domain + void MonFinish( + int limbs, // Number of limbs in each parameter + uint32_t *n, // Large number, buffer size = limbs + const uint32_t *modulus, // Large number, buffer size = limbs + uint32_t mod_inv); // MonModInv() return + + // Computes: result = base ^ exponent (Mod modulus) + // Using Montgomery multiplication with simple squaring method + // Base parameter must be a Montgomery Residue created with MonInputResidue() + void MonExpMod( + const uint32_t *base, // Base for exponentiation, buffer size = mod_limbs + const uint32_t *exponent,// Exponent, buffer size = exponent_limbs + int exponent_limbs, // Number of limbs in exponent + const uint32_t *modulus, // Modulus, buffer size = mod_limbs + int mod_limbs, // Number of limbs in modulus + uint32_t mod_inv, // MonModInv() return + uint32_t *result); // Result, buffer size = mod_limbs + + // Computes: result = base ^ exponent (Mod modulus) + // Using Montgomery multiplication with simple squaring method + void ExpMod( + const uint32_t *base, // Base for exponentiation, buffer size = base_limbs + int base_limbs, // Number of limbs in base + const uint32_t *exponent,// Exponent, buffer size = exponent_limbs + int exponent_limbs, // Number of limbs in exponent + const uint32_t *modulus, // Modulus, buffer size = mod_limbs + int mod_limbs, // Number of limbs in modulus + uint32_t mod_inv, // MonModInv() return + uint32_t *result); // Result, buffer size = mod_limbs + + // Computes: result = base ^ exponent (Mod modulus=mod_p*mod_q) + // Using Montgomery multiplication with Chinese Remainder Theorem + void ExpCRT( + const uint32_t *base, // Base for exponentiation, buffer size = base_limbs + int base_limbs, // Number of limbs in base + const uint32_t *exponent,// Exponent, buffer size = exponent_limbs + int exponent_limbs, // Number of limbs in exponent + const uint32_t *mod_p, // Large number, factorization of modulus, buffer size = p_limbs + uint32_t p_inv, // MonModInv() return + const uint32_t *mod_q, // Large number, factorization of modulus, buffer size = q_limbs + uint32_t q_inv, // MonModInv() return + const uint32_t *pinvq, // Large number, InvMod(p, q) precalculated, buffer size = phi_limbs + int mod_limbs, // Number of limbs in p, q and phi + uint32_t *result); // Result, buffer size = mod_limbs*2 +} + +#endif // include guard diff --git a/Multiplayer/raknet/BitStream.h b/Multiplayer/raknet/BitStream.h index 0f4b57447..9effd8a6c 100644 --- a/Multiplayer/raknet/BitStream.h +++ b/Multiplayer/raknet/BitStream.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -23,10 +23,12 @@ #ifndef __BITSTREAM_H #define __BITSTREAM_H +#include "RakMemoryOverride.h" #include "RakNetDefines.h" #include "Export.h" #include "RakNetTypes.h" -#include +#include "RakString.h" +#include "RakAssert.h" #include #include @@ -34,28 +36,25 @@ #pragma warning( push ) #endif -/// Arbitrary size, just picking something likely to be larger than most packets -#define BITSTREAM_STACK_ALLOCATION_SIZE 256 - /// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common. /// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes. namespace RakNet { - /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. + /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. /// \sa BitStreamSample.txt class RAK_DLL_EXPORT BitStream { - + public: - /// Default Constructor + /// Default Constructor BitStream(); - + /// Create the bitstream, with some number of bytes to immediately allocate. /// There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. /// In that case all it does is save you one or more realloc calls. - /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. - BitStream( int initialBytesToAllocate ); - + /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. + BitStream( const unsigned int initialBytesToAllocate ); + /// Initialize the BitStream, immediately setting the data it contains to a predefined pointer. /// Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory @@ -66,11 +65,11 @@ namespace RakNet /// \param[in] _data An array of bytes. /// \param[in] lengthInBytes Size of the \a _data. /// \param[in] _copyData true or false to make a copy of \a _data or not. - BitStream( unsigned char* _data, unsigned int lengthInBytes, bool _copyData ); - - /// Destructor + BitStream( unsigned char* _data, const unsigned int lengthInBytes, bool _copyData ); + + /// Destructor ~BitStream(); - + /// Resets the bitstream for reuse. void Reset( void ); @@ -125,10 +124,10 @@ namespace RakNet /// Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] input a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes + /// \param[in] input a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool Serialize(bool writeToBitstream, char* input, const int numberOfBytes ); + bool Serialize(bool writeToBitstream, char* input, const unsigned int numberOfBytes ); /// Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. /// Accurate to 1/32767.5. @@ -165,7 +164,7 @@ namespace RakNet /// Lossy, although the result is renormalized template // templateType for this function must be a float or double bool SerializeOrthMatrix( - bool writeToBitstream, + bool writeToBitstream, templateType &m00, templateType &m01, templateType &m02, templateType &m10, templateType &m11, templateType &m12, templateType &m20, templateType &m21, templateType &m22 ); @@ -177,17 +176,22 @@ namespace RakNet /// writing user data, and false when copying bitstream data, such /// as writing one bitstream to another /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data - /// \param[in] input The data - /// \param[in] numberOfBitsToSerialize The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned + /// \param[in] input The data + /// \param[in] numberOfBitsToSerialize The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. - bool SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits = true ); + bool SerializeBits(bool writeToBitstream, unsigned char* input, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits = true ); /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. /// \param[in] var The value to write template void Write(templateType var); + /// Write the dereferenced pointer to any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] var The value to write + template + void WritePtr(templateType *var); + /// Write any integral type to a bitstream. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written /// \param[in] currentValue The current value to write @@ -227,6 +231,11 @@ namespace RakNet template bool Read(templateType &var); + /// Read into a pointer to any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] var The value to read + template + bool ReadPtr(templateType *var); + /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, /// var will be updated. Otherwise it will retain the current value. /// ReadDelta is only valid from a previous call to WriteDelta @@ -253,17 +262,27 @@ namespace RakNet template bool ReadCompressedDelta(templateType &var); + /// Read one bitstream to another + /// \param[in] numberOfBits bits to read + /// \param bitStream the bitstream to read into from + bool Read( BitStream *bitStream, BitSize_t numberOfBits ); + bool Read( BitStream *bitStream ); + bool Read( BitStream &bitStream, BitSize_t numberOfBits ); + bool Read( BitStream &bitStream ); + /// Write an array or casted stream or raw data. This does NOT do endian swapping. - /// \param[in] input a byte buffer - /// \param[in] numberOfBytes the size of \a input in bytes - void Write( const char* input, const int numberOfBytes ); - + /// \param[in] input a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + void Write( const char* input, const unsigned int numberOfBytes ); + /// Write one bitstream to another /// \param[in] numberOfBits bits to write /// \param bitStream the bitstream to copy from - void Write( BitStream *bitStream, int numberOfBits ); + void Write( BitStream *bitStream, BitSize_t numberOfBits ); void Write( BitStream *bitStream ); - + void Write( BitStream &bitStream, BitSize_t numberOfBits ); + void Write( BitStream &bitStream ); + /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. /// Accurate to 1/32767.5. /// \param[in] x x @@ -292,17 +311,17 @@ namespace RakNet /// for 6 bytes instead of 36 /// Lossy, although the result is renormalized template // templateType for this function must be a float or double - void WriteOrthMatrix( + void WriteOrthMatrix( templateType m00, templateType m01, templateType m02, templateType m10, templateType m11, templateType m12, templateType m20, templateType m21, templateType m22 ); /// Read an array or casted stream of byte. The array /// is raw data. There is no automatic endian conversion with this function - /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. + /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. /// \param[in] numberOfBytes The number of byte to read - /// \return true on success false if there is some missing bytes. - bool Read( char* output, const int numberOfBytes ); + /// \return true on success false if there is some missing bytes. + bool Read( char* output, const unsigned int numberOfBytes ); /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. /// Accurate to 1/32767.5. @@ -319,7 +338,7 @@ namespace RakNet /// \param[in] z z template // templateType for this function must be a float or double bool ReadVector( templateType &x, templateType &y, templateType &z ); - + /// Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. /// \param[in] w w /// \param[in] x x @@ -327,143 +346,174 @@ namespace RakNet /// \param[in] z z template // templateType for this function must be a float or double bool ReadNormQuat( templateType &w, templateType &x, templateType &y, templateType &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 /// Lossy, although the result is renormalized template // templateType for this function must be a float or double - bool ReadOrthMatrix( + bool ReadOrthMatrix( templateType &m00, templateType &m01, templateType &m02, templateType &m10, templateType &m11, templateType &m12, templateType &m20, templateType &m21, templateType &m22 ); - + ///Sets the read pointer back to the beginning of your data. void ResetReadPointer( void ); - + /// Sets the write pointer back to the beginning of your data. void ResetWritePointer( void ); - + ///This is good to call when you are done with the stream to make /// sure you didn't leave any data left over void void AssertStreamEmpty( void ); - - /// printf the bits in the stream. Great for debugging. + + /// RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. + void PrintBits( char *out ) const; void PrintBits( void ) const; - + /// Ignore data we don't intend to read /// \param[in] numberOfBits The number of bits to ignore - void IgnoreBits( const int numberOfBits ); - - ///Move the write pointer to a position on the array. - /// \param[in] offset the offset from the start of the array. - /// \attention - /// Dangerous if you don't know what you are doing! + void IgnoreBits( const BitSize_t numberOfBits ); + + /// Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bytes to ignore + void IgnoreBytes( const unsigned int numberOfBytes ); + + ///Move the write pointer to a position on the array. + /// \param[in] offset the offset from the start of the array. + /// \attention + /// Dangerous if you don't know what you are doing! /// For efficiency reasons you can only write mid-stream if your data is byte aligned. - void SetWriteOffset( const int offset ); - + void SetWriteOffset( const BitSize_t offset ); + /// Returns the length in bits of the stream - inline int GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} - inline int GetWriteOffset( void ) const {return numberOfBitsUsed;} - + inline BitSize_t GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} + inline BitSize_t GetWriteOffset( void ) const {return numberOfBitsUsed;} + ///Returns the length in bytes of the stream - inline int GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} - + inline BitSize_t GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} + ///Returns the number of bits into the stream that we have read - inline int GetReadOffset( void ) const {return readOffset;} + inline BitSize_t GetReadOffset( void ) const {return readOffset;} // Sets the read bit index - inline void SetReadOffset( int newReadOffset ) {readOffset=newReadOffset;} - + void SetReadOffset( const BitSize_t newReadOffset ) {readOffset=newReadOffset;} + ///Returns the number of bits left in the stream that haven't been read - inline int GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} - + inline BitSize_t GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} + /// Makes a copy of the internal data for you \a _data will point to /// the stream. Returns the length in bits of the stream. Partial - /// bytes are left aligned + /// bytes are left aligned /// \param[out] _data The allocated copy of GetData() - int CopyData( unsigned char** _data ) const; - + BitSize_t CopyData( unsigned char** _data ) const; + /// Set the stream to some initial data. /// \internal void SetData( unsigned char *input ); - + /// Gets the data that BitStream is writing to / reading from /// Partial bytes are left aligned. - /// \return A pointer to the internal state + /// \return A pointer to the internal state inline unsigned char* GetData( void ) const {return data;} - + /// Write numberToWrite bits from the input source Right aligned /// data means in the case of a partial byte, the bits are aligned /// from the right (bit 0) rather than the left (as in the normal /// internal representation) You would set this to true when /// writing user data, and false when copying bitstream data, such /// as writing one bitstream to another - /// \param[in] input The data - /// \param[in] numberOfBitsToWrite The number of bits to write - /// \param[in] rightAlignedBits if true data will be right aligned - void WriteBits( const unsigned char* input, int numberOfBitsToWrite, const bool rightAlignedBits = true ); - + /// \param[in] input The data + /// \param[in] numberOfBitsToWrite The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + void WriteBits( const unsigned char* input, BitSize_t numberOfBitsToWrite, const bool rightAlignedBits = true ); + /// Align the bitstream to the byte boundary and then write the /// specified number of bits. This is faster than WriteBits but /// wastes the bits to do the alignment and requires you to call /// ReadAlignedBits at the corresponding read position. /// \param[in] input The data - /// \param[in] numberOfBytesToWrite The size of data. - void WriteAlignedBytes( const unsigned char *input, const int numberOfBytesToWrite ); - + /// \param[in] numberOfBytesToWrite The size of input. + void WriteAlignedBytes( const unsigned char *input, const unsigned int numberOfBytesToWrite ); + + /// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite + /// \param[in] input The data + /// \param[in] inputLength The size of input. + /// \param[in] maxBytesToWrite Max bytes to write + void WriteAlignedBytesSafe( const char *input, const unsigned int inputLength, const unsigned int maxBytesToWrite ); + /// Read bits, starting at the next aligned bits. Note that the /// modulus 8 starting offset of the sequence must be the same as /// was used with WriteBits. This will be a problem with packet /// coalescence unless you byte align the coalesced packets. /// \param[in] output The byte array larger than @em numberOfBytesToRead - /// \param[in] numberOfBytesToRead The number of byte to read from the internal state - /// \return true if there is enough byte. - bool ReadAlignedBytes( unsigned char *output, const int numberOfBytesToRead ); - + /// \param[in] numberOfBytesToRead The number of byte to read from the internal state + /// \return true if there is enough byte. + bool ReadAlignedBytes( unsigned char *output, const unsigned int numberOfBytesToRead ); + + /// Reads what was written by WriteAlignedBytesSafe + /// \param[in] input The data + /// \param[in] maxBytesToRead Maximum number of bytes to read + bool ReadAlignedBytesSafe( char *input, int &inputLength, const int maxBytesToRead ); + bool ReadAlignedBytesSafe( char *input, unsigned int &inputLength, const unsigned int maxBytesToRead ); + + /// Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to + /// \param[in] input input will be deleted if it is not a pointer to 0 + bool ReadAlignedBytesSafeAlloc( char **input, int &inputLength, const unsigned int maxBytesToRead ); + bool ReadAlignedBytesSafeAlloc( char **input, unsigned int &inputLength, const unsigned int maxBytesToRead ); + /// Align the next write and/or read to a byte boundary. This can /// be used to 'waste' bits to byte align for efficiency reasons It /// can also be used to force coalesced bitstreams to start on byte /// boundaries so so WriteAlignedBits and ReadAlignedBits both /// calculate the same offset when aligning. void AlignWriteToByteBoundary( void ); - + /// Align the next write and/or read to a byte boundary. This can /// be used to 'waste' bits to byte align for efficiency reasons It /// can also be used to force coalesced bitstreams to start on byte /// boundaries so so WriteAlignedBits and ReadAlignedBits both /// calculate the same offset when aligning. void AlignReadToByteBoundary( void ); - + /// Read \a numberOfBitsToRead bits to the output source /// alignBitsToRight should be set to true to convert internal /// bitstream data to userdata. It should be false if you used /// WriteBits with rightAlignedBits false - /// \param[in] output The resulting bits array - /// \param[in] numberOfBitsToRead The number of bits to read - /// \param[in] alignBitsToRight if true bits will be right aligned. - /// \return true if there is enough bits to read - bool ReadBits( unsigned char *output, int numberOfBitsToRead, const bool alignBitsToRight = true ); - - /// Write a 0 + /// \param[in] output The resulting bits array + /// \param[in] numberOfBitsToRead The number of bits to read + /// \param[in] alignBitsToRight if true bits will be right aligned. + /// \return true if there is enough bits to read + bool ReadBits( unsigned char *output, BitSize_t numberOfBitsToRead, const bool alignBitsToRight = true ); + + /// Write a 0 void Write0( void ); - - /// Write a 1 + + /// Write a 1 void Write1( void ); - + /// Reads 1 bit and returns true if that bit is 1 and false if it is 0 bool ReadBit( void ); - + /// If we used the constructor version with copy data off, this /// *makes sure it is set to on and the data pointed to is copied. void AssertCopyData( void ); - + /// Use this if you pass a pointer copy to the constructor /// *(_copyData==false) and want to overallocate to prevent /// *reallocation - void SetNumberOfBitsAllocated( const unsigned int lengthInBits ); + void SetNumberOfBitsAllocated( const BitSize_t lengthInBits ); - /// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite - void AddBitsAndReallocate( const int numberOfBitsToWrite ); + /// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite + void AddBitsAndReallocate( const BitSize_t numberOfBitsToWrite ); + + /// \internal + /// \return How many bits have been allocated internally + BitSize_t GetNumberOfBitsAllocated(void) const; + + + // Read strings, non reference + bool Read(char *var); + bool Read(unsigned char *var); /// ---- Member function template specialization declarations ---- @@ -479,6 +529,29 @@ namespace RakNet template <> void Write(SystemAddress var); + /// Write a RakNetGUID to a bitsteam + /// \param[in] var The value to write + template <> + void Write(RakNetGuid var); + + /// Write an networkID to a bitstream + /// \param[in] var The value to write + template <> + void Write(NetworkID var); + + /// Write a string to a bitstream + /// \param[in] var The value to write + template <> + void Write(const char* var); + template <> + void Write(const unsigned char* var); + template <> + void Write(char* var); + template <> + void Write(unsigned char* var); + template <> + void Write(RakString var); + /// Write a systemAddress. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written /// \param[in] currentValue The current value to write @@ -486,10 +559,8 @@ namespace RakNet template <> void WriteDelta(SystemAddress currentValue, SystemAddress lastValue); - /// Write an networkID to a bitstream - /// \param[in] var The value to write template <> - void Write(NetworkID var); + void WriteDelta(RakNetGUID currentValue, RakNetGUID lastValue); /// Write an networkID. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written @@ -507,6 +578,9 @@ namespace RakNet template <> void WriteCompressed(SystemAddress var); + template <> + void WriteCompressed(RakNetGUID var); + template <> void WriteCompressed(NetworkID var); @@ -521,6 +595,18 @@ namespace RakNet template <> void WriteCompressed(double var); + /// Compressed string + template <> + void WriteCompressed(const char* var); + template <> + void WriteCompressed(const unsigned char* var); + template <> + void WriteCompressed(char* var); + template <> + void WriteCompressed(unsigned char* var); + template <> + void WriteCompressed(RakString var); + /// Write a bool delta. Same thing as just calling Write /// \param[in] currentValue The current value to write /// \param[in] lastValue The last value to compare against @@ -541,11 +627,23 @@ namespace RakNet template <> bool Read(SystemAddress &var); + template <> + bool Read(RakNetGUID &var); + /// Read an NetworkID from a bitstream /// \param[in] var The value to read template <> bool Read(NetworkID &var); + /// Read a String from a bitstream + /// \param[in] var The value to read + template <> + bool Read(char *&var); + template <> + bool Read(unsigned char *&var); + template <> + bool Read(RakString &var); + /// Read a bool from a bitstream /// \param[in] var The value to read template <> @@ -554,6 +652,9 @@ namespace RakNet template <> bool ReadCompressed(SystemAddress &var); + template <> + bool ReadCompressed(RakNetGUID &var); + template <> bool ReadCompressed(NetworkID &var); @@ -565,41 +666,49 @@ namespace RakNet /// For values between -1 and 1 template <> - bool ReadCompressed(double &var); + bool ReadCompressed(double &var); + + template <> + bool ReadCompressed(char* &var); + template <> + bool ReadCompressed(unsigned char *&var); + template <> + bool ReadCompressed(RakString &var); /// Read a bool from a bitstream /// \param[in] var The value to read template <> bool ReadCompressedDelta(bool &var); #endif + + static bool DoEndianSwap(void); + static bool IsBigEndian(void); + static bool IsNetworkOrder(void); + static void ReverseBytes(unsigned char *input, unsigned char *output, const unsigned int length); + static void ReverseBytesInPlace(unsigned char *data,const unsigned int length); + private: BitStream( const BitStream &invalid) { - #ifdef _MSC_VER - #pragma warning(disable:4100) - // warning C4100: 'invalid' : unreferenced formal parameter - #endif - + (void) invalid; + RakAssert(0); } /// 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 ); - + void WriteCompressed( const unsigned char* input, const unsigned 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 ); - - void ReverseBytes(unsigned char *input, unsigned char *output, int length); + bool ReadCompressed( unsigned char* output, const unsigned int size, const bool unsignedData ); + + + BitSize_t numberOfBitsUsed; + + BitSize_t numberOfBitsAllocated; + + BitSize_t readOffset; - bool DoEndianSwap(void) const; - - int numberOfBitsUsed; - - int numberOfBitsAllocated; - - int readOffset; - unsigned char *data; - + /// true if the internal buffer is copy of the data passed to the constructor bool copyData; @@ -667,7 +776,7 @@ namespace RakNet return true; } - inline bool BitStream::Serialize(bool writeToBitstream, char* input, const int numberOfBytes ) + inline bool BitStream::Serialize(bool writeToBitstream, char* input, const unsigned int numberOfBytes ) { if (writeToBitstream) Write(input, numberOfBytes); @@ -708,7 +817,7 @@ namespace RakNet template inline bool BitStream::SerializeOrthMatrix( - bool writeToBitstream, + bool writeToBitstream, templateType &m00, templateType &m01, templateType &m02, templateType &m10, templateType &m11, templateType &m12, templateType &m20, templateType &m21, templateType &m22 ) @@ -720,7 +829,7 @@ namespace RakNet return true; } - inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits ) + inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* input, const BitSize_t numberOfBitsToSerialize, const bool rightAlignedBits ) { if (writeToBitstream) WriteBits(input,numberOfBitsToSerialize,rightAlignedBits); @@ -752,6 +861,29 @@ namespace RakNet } } + template + inline void BitStream::WritePtr(templateType *var) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(var)==1) + WriteBits( ( unsigned char* ) var, sizeof( templateType ) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + ReverseBytes((unsigned char*) var, output, sizeof(templateType)); + WriteBits( ( unsigned char* ) output, sizeof(templateType) * 8, true ); + } + else +#endif + WriteBits( ( unsigned char* ) var, sizeof(templateType) * 8, true ); + } + } + /// Write a bool to a bitstream /// \param[in] var The value to write template <> @@ -768,21 +900,80 @@ namespace RakNet template <> inline void BitStream::Write(SystemAddress var) { - // Write(var.binaryAddress); - WriteBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); + // Hide the address so routers don't modify it + var.binaryAddress=~var.binaryAddress; + // Don't endian swap the address + WriteBits((unsigned char*)&var.binaryAddress, sizeof(var.binaryAddress)*8, true); Write(var.port); } + template <> + inline void BitStream::Write(RakNetGUID var) + { + for (unsigned int i=0; i < sizeof(var.g) / sizeof(var.g[0]); i++) + Write(var.g[i]); + } + /// Write an networkID to a bitstream /// \param[in] var The value to write template <> inline void BitStream::Write(NetworkID var) { - if (NetworkID::IsPeerToPeerMode()) // Use the function rather than directly access the member or DLL users will get an undefined external error - Write(var.systemAddress); +#if defined (NETWORK_ID_SUPPORTS_PEER_TO_PEER) + RakAssert(NetworkID::IsPeerToPeerMode()); +// if (NetworkID::IsPeerToPeerMode()) // Use the function rather than directly access the member or DLL users will get an undefined external error + { + if (var.guid!=UNASSIGNED_RAKNET_GUID) + { + Write(true); + Write(var.guid); + } + else + Write(false); + if (var.systemAddress!=UNASSIGNED_SYSTEM_ADDRESS) + { + Write(true); + Write(var.systemAddress); + } + else + Write(false); + } +#endif + /* + Write(var.guid); + Write(var.systemAddress); + */ Write(var.localSystemAddress); } + /// Write a string to a bitstream + /// \param[in] var The value to write + template <> + inline void BitStream::Write(RakString var) + { + var.Serialize(this); + } + template <> + inline void BitStream::Write(const char * var) + { + RakString::Serialize(var, this); + } + template <> + inline void BitStream::Write(const unsigned char * var) + { + Write((const char*)var); + } + template <> + inline void BitStream::Write(char * var) + { + Write((const char*)var); + } + template <> + inline void BitStream::Write(unsigned char * var) + { + Write((const char*)var); + } + /// Write any integral type to a bitstream. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written /// \param[in] currentValue The current value to write @@ -815,11 +1006,24 @@ namespace RakNet else { Write(true); - Write(currentValue.binaryAddress); - Write(currentValue.port); + Write(currentValue); } } + template <> + inline void BitStream::WriteDelta(RakNetGUID currentValue, RakNetGUID lastValue) + { + if (currentValue==lastValue) + { + Write(false); + } + else + { + Write(true); + Write(currentValue); + } + } + /// Write a systemAddress. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written /// \param[in] currentValue The current value to write @@ -834,9 +1038,7 @@ namespace RakNet else { Write(true); - if (NetworkID::IsPeerToPeerMode()) // Use the function rather than directly access the member or DLL users will get an undefined external error - Write(currentValue.systemAddress); - Write(currentValue.localSystemAddress); + Write(currentValue); } } @@ -846,9 +1048,8 @@ namespace RakNet template <> inline void BitStream::WriteDelta(bool currentValue, bool lastValue) { -#ifdef _MSC_VER -#pragma warning(disable:4100) // warning C4100: 'lastValue' : unreferenced formal parameter -#endif + (void) lastValue; + Write(currentValue); } @@ -899,6 +1100,12 @@ namespace RakNet Write(var); } + template <> + inline void BitStream::WriteCompressed(RakNetGUID var) + { + Write(var); + } + template <> inline void BitStream::WriteCompressed(NetworkID var) { @@ -915,7 +1122,7 @@ namespace RakNet template <> inline void BitStream::WriteCompressed(float var) { - assert(var > -1.01f && var < 1.01f); + RakAssert(var > -1.01f && var < 1.01f); if (var < -1.0f) var=-1.0f; if (var > 1.0f) @@ -927,17 +1134,44 @@ namespace RakNet template <> inline void BitStream::WriteCompressed(double var) { - assert(var > -1.01 && var < 1.01); + RakAssert(var > -1.01 && var < 1.01); if (var < -1.0f) var=-1.0f; if (var > 1.0f) var=1.0f; #ifdef _DEBUG - assert(sizeof(unsigned long)==4); + RakAssert(sizeof(unsigned long)==4); #endif Write((unsigned long)((var+1.0)*2147483648.0)); } + /// Compress the string + template <> + inline void BitStream::WriteCompressed(RakString var) + { + var.SerializeCompressed(this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const char * var) + { + RakString::SerializeCompressed(var,this,0,false); + } + template <> + inline void BitStream::WriteCompressed(const unsigned char * var) + { + WriteCompressed((const char*) var); + } + template <> + inline void BitStream::WriteCompressed(char * var) + { + WriteCompressed((const char*) var); + } + template <> + inline void BitStream::WriteCompressed(unsigned char * var) + { + WriteCompressed((const char*) var); + } + /// Write any integral type to a bitstream. If the current value is different from the last value /// the current value will be written. Otherwise, a single bit will be written /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. @@ -965,9 +1199,8 @@ namespace RakNet template <> inline void BitStream::WriteCompressedDelta(bool currentValue, bool lastValue) { -#ifdef _MSC_VER -#pragma warning(disable:4100) // warning C4100: 'lastValue' : unreferenced formal parameter -#endif + (void) lastValue; + Write(currentValue); } @@ -1018,6 +1251,36 @@ namespace RakNet } } + template + inline bool BitStream::ReadPtr(templateType *var) + { +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + if (sizeof(var)==1) + return ReadBits( ( unsigned char* ) var, sizeof(templateType) * 8, true ); + else + { +#ifndef __BITSTREAM_NATIVE_END +#ifdef _MSC_VER +#pragma warning(disable:4244) // '=' : conversion from 'unsigned long' to 'unsigned short', possible loss of data +#endif + if (DoEndianSwap()) + { + unsigned char output[sizeof(templateType)]; + if (ReadBits( ( unsigned char* ) output, sizeof(templateType) * 8, true )) + { + ReverseBytes(output, (unsigned char*)var, sizeof(templateType)); + return true; + } + return false; + } + else +#endif + return ReadBits( ( unsigned char* ) var, sizeof(templateType) * 8, true ); + } + } + /// Read a bool from a bitstream /// \param[in] var The value to read template <> @@ -1042,21 +1305,70 @@ namespace RakNet template <> inline bool BitStream::Read(SystemAddress &var) { - // Read(var.binaryAddress); - ReadBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); + //Read(var.binaryAddress); + // Don't endian swap the address + ReadBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); + // Unhide the IP address, done to prevent routers from changing it + var.binaryAddress=~var.binaryAddress; return Read(var.port); } + template <> + inline bool BitStream::Read(RakNetGUID &var) + { + bool b=true; + for (unsigned int i=0; i < sizeof(var.g) / sizeof(var.g[0]); i++) + b=Read(var.g[i]); + return b; + } + /// Read an networkID from a bitstream /// \param[in] var The value to read template <> inline bool BitStream::Read(NetworkID &var) { - if (NetworkID::IsPeerToPeerMode()) // Use the function rather than directly access the member or DLL users will get an undefined external error - Read(var.systemAddress); +#if defined (NETWORK_ID_SUPPORTS_PEER_TO_PEER) + RakAssert(NetworkID::IsPeerToPeerMode()); + //if (NetworkID::IsPeerToPeerMode()) // Use the function rather than directly access the member or DLL users will get an undefined external error + { + bool hasGuid, hasSystemAddress; + Read(hasGuid); + if (hasGuid) + Read(var.guid); + else + var.guid=UNASSIGNED_RAKNET_GUID; + Read(hasSystemAddress); + if (hasSystemAddress) + Read(var.systemAddress); + else + var.systemAddress=UNASSIGNED_SYSTEM_ADDRESS; + } +#endif + /* + Read(var.guid); + Read(var.systemAddress); + */ return Read(var.localSystemAddress); } + /// Read an networkID from a bitstream + /// \param[in] var The value to read + template <> + inline bool BitStream::Read(RakString &var) + { + return var.Deserialize(this); + } + template <> + inline bool BitStream::Read(char *&var) + { + return RakString::Deserialize(var,this); + } + template <> + inline bool BitStream::Read(unsigned char *&var) + { + return RakString::Deserialize((char*) var,this); + } + /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, /// var will be updated. Otherwise it will retain the current value. /// ReadDelta is only valid from a previous call to WriteDelta @@ -1118,6 +1430,12 @@ namespace RakNet return Read(var); } + template <> + inline bool BitStream::ReadCompressed(RakNetGUID &var) + { + return Read(var); + } + template <> inline bool BitStream::ReadCompressed(NetworkID &var) { @@ -1156,6 +1474,22 @@ namespace RakNet return false; } + /// For strings + template <> + inline bool BitStream::ReadCompressed(RakString &var) + { + return var.DeserializeCompressed(this,false); + } + template <> + inline bool BitStream::ReadCompressed(char *&var) + { + return RakString::DeserializeCompressed(var,this,false); + } + template <> + inline bool BitStream::ReadCompressed(unsigned char *&var) + { + return RakString::DeserializeCompressed((char*) var,this,false); + } /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, /// var will be updated. Otherwise it will retain the current value. @@ -1189,7 +1523,7 @@ namespace RakNet void BitStream::WriteNormVector( templateType x, templateType y, templateType z ) { #ifdef _DEBUG - assert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); + RakAssert(x <= 1.01 && y <= 1.01 && z <= 1.01 && x >= -1.01 && y >= -1.01 && z >= -1.01); #endif if (x>1.0) x=1.0; @@ -1228,7 +1562,7 @@ namespace RakNet { templateType magnitude = sqrt(x * x + y * y + z * z); Write((float)magnitude); - if (magnitude > 0.0) + if (magnitude > 0.00001f) { WriteCompressed((float)(x/magnitude)); WriteCompressed((float)(y/magnitude)); @@ -1253,32 +1587,29 @@ namespace RakNet } template // templateType for this function must be a float or double - void BitStream::WriteOrthMatrix( + void BitStream::WriteOrthMatrix( templateType m00, templateType m01, templateType m02, templateType m10, templateType m11, templateType m12, templateType m20, templateType m21, templateType m22 ) { + double qw; double qx; double qy; double qz; -#ifdef _MSC_VER -#pragma warning(disable:4100) // m10, m01 : unreferenced formal parameter -#endif - // Convert matrix to quat // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/ float sum; sum = 1 + m00 + m11 + m22; if (sum < 0.0f) sum=0.0f; - qw = sqrt( sum ) / 2; + qw = sqrt( sum ) / 2; sum = 1 + m00 - m11 - m22; if (sum < 0.0f) sum=0.0f; - qx = sqrt( sum ) / 2; + qx = sqrt( sum ) / 2; sum = 1 - m00 + m11 - m22; if (sum < 0.0f) sum=0.0f; - qy = sqrt( sum ) / 2; + qy = sqrt( sum ) / 2; sum = 1 - m00 - m11 + m22; if (sum < 0.0f) sum=0.0f; qz = sqrt( sum ) / 2; @@ -1343,7 +1674,7 @@ namespace RakNet //unsigned short sx,sy,sz; if (!Read(magnitude)) return false; - if (magnitude!=0.0) + if (magnitude>0.00001f) { // Read(sx); // Read(sy); @@ -1394,17 +1725,18 @@ namespace RakNet if (cxNeg) x=-x; if (cyNeg) y=-y; if (czNeg) z=-z; - float difference = 1.0 - x*x - y*y - z*z; + float difference = 1.0f - x*x - y*y - z*z; if (difference < 0.0f) difference=0.0f; w = (templateType)(sqrt(difference)); if (cwNeg) w=-w; + return true; } template // templateType for this function must be a float or double - bool BitStream::ReadOrthMatrix( + bool BitStream::ReadOrthMatrix( templateType &m00, templateType &m01, templateType &m02, templateType &m10, templateType &m11, templateType &m12, templateType &m20, templateType &m21, templateType &m22 ) @@ -1439,12 +1771,27 @@ namespace RakNet return true; } + + template + BitStream& operator<<(BitStream& out, templateType& c) + { + out.Write(c); + return out; + } + template + BitStream& operator>>(BitStream& in, templateType& c) + { + bool success = in.Read(c); + RakAssert(success); + return in; + } + } #ifdef _MSC_VER #pragma warning( pop ) #endif -#endif +#endif #endif // VC6 diff --git a/Multiplayer/raknet/BitStream_NoTemplate.h b/Multiplayer/raknet/BitStream_NoTemplate.h new file mode 100644 index 000000000..79aca6c47 --- /dev/null +++ b/Multiplayer/raknet/BitStream_NoTemplate.h @@ -0,0 +1,784 @@ +/// \file +/// \brief This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#if defined(_MSC_VER) && _MSC_VER < 1299 // VC6 doesn't support template specialization + +#ifndef __BITSTREAM_H +#define __BITSTREAM_H + +#include "RakMemoryOverride.h" +#include "RakNetDefines.h" +#include "Export.h" +#include "RakNetTypes.h" +#include "RakAssert.h" +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +#include +#else +#include +#endif +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// Arbitrary size, just picking something likely to be larger than most packets +#define BITSTREAM_STACK_ALLOCATION_SIZE 256 + +/// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common. +/// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes. +namespace RakNet +{ + /// This class allows you to write and read native types as a string of bits. BitStream is used extensively throughout RakNet and is designed to be used by users as well. + /// \sa BitStreamSample.txt + class RAK_DLL_EXPORT BitStream + { + + public: + /// Default Constructor + BitStream(); + + /// Create the bitstream, with some number of bytes to immediately allocate. + /// There is no benefit to calling this, unless you know exactly how many bytes you need and it is greater than BITSTREAM_STACK_ALLOCATION_SIZE. + /// In that case all it does is save you one or more realloc calls. + /// \param[in] initialBytesToAllocate the number of bytes to pre-allocate. + BitStream( int initialBytesToAllocate ); + + /// Initialize the BitStream, immediately setting the data it contains to a predefined pointer. + /// Set \a _copyData to true if you want to make an internal copy of the data you are passing. Set it to false to just save a pointer to the data. + /// You shouldn't call Write functions with \a _copyData as false, as this will write to unallocated memory + /// 99% of the time you will use this function to cast Packet::data to a bitstream for reading, in which case you should write something as follows: + /// \code + /// RakNet::BitStream bs(packet->data, packet->length, false); + /// \endcode + /// \param[in] _data An array of bytes. + /// \param[in] lengthInBytes Size of the \a _data. + /// \param[in] _copyData true or false to make a copy of \a _data or not. + BitStream( unsigned char* _data, unsigned int lengthInBytes, bool _copyData ); + + /// Destructor + ~BitStream(); + + /// Resets the bitstream for reuse. + void Reset( void ); + + /// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] var The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool Serialize(bool writeToBitstream, bool &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, unsigned char &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, char &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, unsigned short &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, short &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, unsigned int &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, int &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, unsigned long &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, long &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, long long &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, float &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, double &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + bool Serialize(bool writeToBitstream, long double &var){if (writeToBitstream)Write(var);else return Read(var); return true;} + + /// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeDelta(bool writeToBitstream, bool ¤tValue, bool lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned char ¤tValue, unsigned char lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, char ¤tValue, char lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned short ¤tValue, unsigned short lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, short ¤tValue, short lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned int ¤tValue, unsigned int lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, int ¤tValue, int lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned long ¤tValue, unsigned long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, long long ¤tValue, long long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned long long ¤tValue, unsigned long long lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, float ¤tValue, float lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, double ¤tValue, double lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, long double ¤tValue, long double lastValue){if (writeToBitstream) WriteDelta(currentValue, lastValue); else return ReadDelta(currentValue);return true;} + + /// Bidirectional version of SerializeDelta when you don't know what the last value is, or there is no last value. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] currentValue The current value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeDelta(bool writeToBitstream, bool ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned char ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, char ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned short ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, short ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned int ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, int ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned long ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, long long ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, unsigned long long ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, float ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, double ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + bool SerializeDelta(bool writeToBitstream, long double ¤tValue){if (writeToBitstream) WriteDelta(currentValue); else return ReadDelta(currentValue);return true;} + + /// Bidirectional serialize/deserialize any integral type to/from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] var The value to write + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeCompressed(bool writeToBitstream, bool &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, unsigned char &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, char &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, unsigned short &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, short &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, unsigned int &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, int &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, unsigned long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, long long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, float &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, double &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + bool SerializeCompressed(bool writeToBitstream, long double &var){if (writeToBitstream)WriteCompressed(var);else return ReadCompressed(var); return true;} + + /// Bidirectional serialize/deserialize any integral type to/from a bitstream. If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against. Only used if \a writeToBitstream is true. + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeCompressedDelta(bool writeToBitstream, bool ¤tValue, bool lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned char ¤tValue, unsigned char lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, char ¤tValue, char lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned short ¤tValue, unsigned short lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, short ¤tValue, short lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned int ¤tValue, unsigned int lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, int ¤tValue, int lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned long ¤tValue, unsigned long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, long long ¤tValue, long long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned long long ¤tValue, unsigned long long lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, float ¤tValue, float lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, double ¤tValue, double lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + bool SerializeCompressedDelta(bool writeToBitstream, long double ¤tValue, long double lastValue){if (writeToBitstream) WriteCompressedDelta(currentValue, lastValue); else return ReadCompressedDelta(currentValue);return true;} + + /// Save as SerializeCompressedDelta(templateType ¤tValue, templateType lastValue) when we have an unknown second parameter + bool SerializeCompressedDelta(bool writeToBitstream, bool &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned char &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, char &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned short &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, short &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned int &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, int &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, long long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, unsigned long long &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, float &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, double &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + bool SerializeCompressedDelta(bool writeToBitstream, long double &var){if (writeToBitstream)WriteCompressedDelta(var);else return ReadCompressedDelta(var); return true;} + + /// Bidirectional serialize/deserialize an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] input a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool Serialize(bool writeToBitstream, char* input, const int numberOfBytes ); + + /// Bidirectional serialize/deserialize a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeNormVector(bool writeToBitstream, float &x, float &y, float z ){if (writeToBitstream) WriteNormVector(x,y,z); else return ReadNormVector(x,y,z); return true;} + bool SerializeNormVector(bool writeToBitstream, double &x, double &y, double &z ){if (writeToBitstream) WriteNormVector(x,y,z); else return ReadNormVector(x,y,z); return true;} + + /// Bidirectional serialize/deserialize a vector, using 10 bytes instead of 12. + /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeVector(bool writeToBitstream, float &x, float &y, float &z ){if (writeToBitstream) WriteVector(x,y,z); else return ReadVector(x,y,z); return true;} + bool SerializeVector(bool writeToBitstream, double &x, double &y, double &z ){if (writeToBitstream) WriteVector(x,y,z); else return ReadVector(x,y,z); return true;} + + /// Bidirectional serialize/deserialize a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeNormQuat(bool writeToBitstream, float &w, float &x, float &y, float &z){if (writeToBitstream) WriteNormQuat(w,x,y,z); else return ReadNormQuat(w,x,y,z); return true;} + bool SerializeNormQuat(bool writeToBitstream, double &w, double &x, double &y, double &z){if (writeToBitstream) WriteNormQuat(w,x,y,z); else return ReadNormQuat(w,x,y,z); return true;} + + /// Bidirectional serialize/deserialize an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each + /// for 6 bytes instead of 36 + /// Lossy, although the result is renormalized + bool SerializeOrthMatrix( + bool writeToBitstream, + float &m00, float &m01, float &m02, + float &m10, float &m11, float &m12, + float &m20, float &m21, float &m22 ){if (writeToBitstream) WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); else return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); return true;} + bool SerializeOrthMatrix( + bool writeToBitstream, + double &m00, double &m01, double &m02, + double &m10, double &m11, double &m12, + double &m20, double &m21, double &m22 ){if (writeToBitstream) WriteOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); else return ReadOrthMatrix(m00,m01,m02,m10,m11,m12,m20,m21,m22); return true;} + + /// Bidirectional serialize/deserialize numberToSerialize bits to/from the input. Right aligned + /// data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another + /// \param[in] writeToBitstream true to write from your data to this bitstream. False to read from this bitstream and write to your data + /// \param[in] input The data + /// \param[in] numberOfBitsToSerialize The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + /// \return true if \a writeToBitstream is true. true if \a writeToBitstream is false and the read was successful. false if \a writeToBitstream is false and the read was not successful. + bool SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits = true ); + + /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] var The value to write + void Write(bool var){if ( var ) Write1(); else Write0();} + void Write(unsigned char var){WriteBits( ( unsigned char* ) & var, sizeof( unsigned char ) * 8, true );} + void Write(char var){WriteBits( ( unsigned char* ) & var, sizeof( char ) * 8, true );} + void Write(unsigned short var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned short)); WriteBits( ( unsigned char* ) output, sizeof(unsigned short) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );} + void Write(short var) {if (DoEndianSwap()){unsigned char output[sizeof(short)]; ReverseBytes((unsigned char*)&var, output, sizeof(short)); WriteBits( ( unsigned char* ) output, sizeof(short) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(short) * 8, true );} + void Write(unsigned int var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned int)); WriteBits( ( unsigned char* ) output, sizeof(unsigned int) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );} + void Write(int var) {if (DoEndianSwap()){unsigned char output[sizeof(int)]; ReverseBytes((unsigned char*)&var, output, sizeof(int)); WriteBits( ( unsigned char* ) output, sizeof(int) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(int) * 8, true );} + void Write(unsigned long var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long)); WriteBits( ( unsigned char* ) output, sizeof(unsigned long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );} + void Write(long var) {if (DoEndianSwap()){unsigned char output[sizeof(long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long)); WriteBits( ( unsigned char* ) output, sizeof(long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long) * 8, true );} + void Write(long long var) {if (DoEndianSwap()){unsigned char output[sizeof(long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long long)); WriteBits( ( unsigned char* ) output, sizeof(long long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long long) * 8, true );} + void Write(unsigned long long var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long long)); WriteBits( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );} + void Write(float var) {if (DoEndianSwap()){unsigned char output[sizeof(float)]; ReverseBytes((unsigned char*)&var, output, sizeof(float)); WriteBits( ( unsigned char* ) output, sizeof(float) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(float) * 8, true );} + void Write(double var) {if (DoEndianSwap()){unsigned char output[sizeof(double)]; ReverseBytes((unsigned char*)&var, output, sizeof(double)); WriteBits( ( unsigned char* ) output, sizeof(double) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(double) * 8, true );} + void Write(long double var) {if (DoEndianSwap()){unsigned char output[sizeof(long double)]; ReverseBytes((unsigned char*)&var, output, sizeof(long double)); WriteBits( ( unsigned char* ) output, sizeof(long double) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(long double) * 8, true );} + void Write(void* var) {if (DoEndianSwap()){unsigned char output[sizeof(void*)]; ReverseBytes((unsigned char*)&var, output, sizeof(void*)); WriteBits( ( unsigned char* ) output, sizeof(void*) * 8, true );} WriteBits( ( unsigned char* ) & var, sizeof(void*) * 8, true );} + void Write(SystemAddress var){WriteBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); Write(var.port);} + void Write(NetworkID var){if (NetworkID::IsPeerToPeerMode()) Write(var.systemAddress); Write(var.localSystemAddress);} + + /// Write any integral type to a bitstream. If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + void WriteDelta(bool currentValue, bool lastValue){ + #pragma warning(disable:4100) // warning C4100: 'peer' : unreferenced formal parameter + Write(currentValue); + } + void WriteDelta(unsigned char currentValue, unsigned char lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(char currentValue, char lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(unsigned short currentValue, unsigned short lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(short currentValue, short lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(unsigned int currentValue, unsigned int lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(int currentValue, int lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(unsigned long currentValue, unsigned long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(long currentValue, long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(long long currentValue, long long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(unsigned long long currentValue, unsigned long long lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(float currentValue, float lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(double currentValue, double lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(long double currentValue, long double lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(SystemAddress currentValue, SystemAddress lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + void WriteDelta(NetworkID currentValue, NetworkID lastValue){if (currentValue==lastValue) {Write(false);} else {Write(true); Write(currentValue);}} + + /// WriteDelta when you don't know what the last value is, or there is no last value. + /// \param[in] currentValue The current value to write + void WriteDelta(bool var){Write(var);} + void WriteDelta(unsigned char var){Write(true); Write(var);} + void WriteDelta(char var){Write(true); Write(var);} + void WriteDelta(unsigned short var){Write(true); Write(var);} + void WriteDelta(short var){Write(true); Write(var);} + void WriteDelta(unsigned int var){Write(true); Write(var);} + void WriteDelta(int var){Write(true); Write(var);} + void WriteDelta(unsigned long var){Write(true); Write(var);} + void WriteDelta(long var){Write(true); Write(var);} + void WriteDelta(long long var){Write(true); Write(var);} + void WriteDelta(unsigned long long var){Write(true); Write(var);} + void WriteDelta(float var){Write(true); Write(var);} + void WriteDelta(double var){Write(true); Write(var);} + void WriteDelta(long double var){Write(true); Write(var);} + void WriteDelta(SystemAddress var){Write(true); Write(var);} + void WriteDelta(NetworkID var){Write(true); Write(var);} + + /// Write any integral type to a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// \param[in] var The value to write + void WriteCompressed(bool var) {Write(var);} + void WriteCompressed(unsigned char var) {WriteCompressed( ( unsigned char* ) & var, sizeof( unsigned char ) * 8, true );} + void WriteCompressed(char var) {WriteCompressed( (unsigned char* ) & var, sizeof( unsigned char ) * 8, true );} + void WriteCompressed(unsigned short var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned short)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned short)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned short) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );} + void WriteCompressed(short var) {if (DoEndianSwap()) {unsigned char output[sizeof(short)]; ReverseBytes((unsigned char*)&var, output, sizeof(short)); WriteCompressed( ( unsigned char* ) output, sizeof(short) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(short) * 8, true );} + void WriteCompressed(unsigned int var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned int)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned int)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned int) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );} + void WriteCompressed(int var) {if (DoEndianSwap()) { unsigned char output[sizeof(int)]; ReverseBytes((unsigned char*)&var, output, sizeof(int)); WriteCompressed( ( unsigned char* ) output, sizeof(int) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(int) * 8, true );} + void WriteCompressed(unsigned long var) {if (DoEndianSwap()) {unsigned char output[sizeof(unsigned long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );} + void WriteCompressed(long var) {if (DoEndianSwap()) {unsigned char output[sizeof(long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long)); WriteCompressed( ( unsigned char* ) output, sizeof(long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(long) * 8, true );} + void WriteCompressed(long long var) {if (DoEndianSwap()) {unsigned char output[sizeof(long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(long long)); WriteCompressed( ( unsigned char* ) output, sizeof(long long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(long long) * 8, true );} + void WriteCompressed(unsigned long long var) {if (DoEndianSwap()) { unsigned char output[sizeof(unsigned long long)]; ReverseBytes((unsigned char*)&var, output, sizeof(unsigned long long)); WriteCompressed( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true );} else WriteCompressed( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );} + void WriteCompressed(float var) {RakAssert(var > -1.01f && var < 1.01f); if (var < -1.0f) var=-1.0f; if (var > 1.0f) var=1.0f; Write((unsigned short)((var+1.0f)*32767.5f));} + void WriteCompressed(double var) {RakAssert(var > -1.01 && var < 1.01); if (var < -1.0) var=-1.0; if (var > 1.0) var=1.0; Write((unsigned long)((var+1.0)*2147483648.0));} + void WriteCompressed(long double var) {RakAssert(var > -1.01 && var < 1.01); if (var < -1.0) var=-1.0; if (var > 1.0) var=1.0; Write((unsigned long)((var+1.0)*2147483648.0));} + + /// Write any integral type to a bitstream. If the current value is different from the last value + /// the current value will be written. Otherwise, a single bit will be written + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] currentValue The current value to write + /// \param[in] lastValue The last value to compare against + void WriteCompressedDelta(bool currentValue, bool lastValue) + { + #pragma warning(disable:4100) // warning C4100: 'peer' : unreferenced formal parameter + Write(currentValue); + } + void WriteCompressedDelta(unsigned char currentValue, unsigned char lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(char currentValue, char lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(unsigned short currentValue, unsigned short lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(short currentValue, short lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(unsigned int currentValue, unsigned int lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(int currentValue, int lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(unsigned long currentValue, unsigned long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(long currentValue, long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(long long currentValue, long long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(unsigned long long currentValue, unsigned long long lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(float currentValue, float lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(double currentValue, double lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + void WriteCompressedDelta(long double currentValue, long double lastValue){if (currentValue==lastValue) {Write(false);} else { Write(true); WriteCompressed(currentValue);}} + + /// Save as WriteCompressedDelta(templateType currentValue, templateType lastValue) when we have an unknown second parameter + void WriteCompressedDelta(bool var) {Write(var);} + void WriteCompressedDelta(unsigned char var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(char var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(unsigned short var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(short var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(unsigned int var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(int var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(unsigned long var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(long var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(long long var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(unsigned long long var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(float var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(double var) { Write(true); WriteCompressed(var); } + void WriteCompressedDelta(long double var) { Write(true); WriteCompressed(var); } + + /// Read any integral type from a bitstream. Define __BITSTREAM_NATIVE_END if you need endian swapping. + /// \param[in] var The value to read + bool Read(bool &var){if ( readOffset + 1 > numberOfBitsUsed ) return false; + if ( data[ readOffset >> 3 ] & ( 0x80 >> ( readOffset & 7 ) ) ) + var = true; + else + var = false; + // Has to be on a different line for Mac + readOffset++; + return true; + } + bool Read(unsigned char &var) {return ReadBits( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );} + bool Read(char &var) {return ReadBits( ( unsigned char* ) &var, sizeof(char) * 8, true );} + bool Read(unsigned short &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned short) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned short)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );} + bool Read(short &var) {if (DoEndianSwap()){unsigned char output[sizeof(short)]; if (ReadBits( ( unsigned char* ) output, sizeof(short) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(short)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(short) * 8, true );} + bool Read(unsigned int &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned int) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned int)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );} + bool Read(int &var) {if (DoEndianSwap()){unsigned char output[sizeof(int)]; if (ReadBits( ( unsigned char* ) output, sizeof(int) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(int)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(int) * 8, true );} + bool Read(unsigned long &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );} + bool Read(long &var) {if (DoEndianSwap()){unsigned char output[sizeof(long)]; if (ReadBits( ( unsigned char* ) output, sizeof(long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long) * 8, true );} + bool Read(long long &var) {if (DoEndianSwap()){unsigned char output[sizeof(long long)]; if (ReadBits( ( unsigned char* ) output, sizeof(long long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long long) * 8, true );} + bool Read(unsigned long long &var) {if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; if (ReadBits( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long long)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );} + bool Read(float &var) {if (DoEndianSwap()){unsigned char output[sizeof(float)]; if (ReadBits( ( unsigned char* ) output, sizeof(float) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(float)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(float) * 8, true );} + bool Read(double &var) {if (DoEndianSwap()){unsigned char output[sizeof(double)]; if (ReadBits( ( unsigned char* ) output, sizeof(double) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(double)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(double) * 8, true );} + bool Read(long double &var) {if (DoEndianSwap()){unsigned char output[sizeof(long double)]; if (ReadBits( ( unsigned char* ) output, sizeof(long double) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(long double)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(long double) * 8, true );} + bool Read(void* &var) {if (DoEndianSwap()){unsigned char output[sizeof(void*)]; if (ReadBits( ( unsigned char* ) output, sizeof(void*) * 8, true )) { ReverseBytes(output, (unsigned char*)&var, sizeof(void*)); return true;} return false;} else return ReadBits( ( unsigned char* ) & var, sizeof(void*) * 8, true );} + bool Read(SystemAddress &var){ReadBits( ( unsigned char* ) & var.binaryAddress, sizeof(var.binaryAddress) * 8, true ); return Read(var.port);} + bool Read(NetworkID &var){if (NetworkID::IsPeerToPeerMode()) Read(var.systemAddress); return Read(var.localSystemAddress);} + + /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// ReadDelta is only valid from a previous call to WriteDelta + /// \param[in] var The value to read + bool ReadDelta(bool &var) {return Read(var);} + bool ReadDelta(unsigned char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(unsigned short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(unsigned int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(unsigned long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(unsigned long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(float &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(long double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(SystemAddress &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + bool ReadDelta(NetworkID &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=Read(var); return success;} + + + /// Read any integral type from a bitstream. Undefine __BITSTREAM_NATIVE_END if you need endian swapping. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// \param[in] var The value to read + bool ReadCompressed(bool &var) {return Read(var);} + bool ReadCompressed(unsigned char &var) {return ReadCompressed( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );} + bool ReadCompressed(char &var) {return ReadCompressed( ( unsigned char* ) &var, sizeof(unsigned char) * 8, true );} + bool ReadCompressed(unsigned short &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned short)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned short) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned short)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned short) * 8, true );} + bool ReadCompressed(short &var){if (DoEndianSwap()){unsigned char output[sizeof(short)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(short) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(short)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(short) * 8, true );} + bool ReadCompressed(unsigned int &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned int)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned int) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned int)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned int) * 8, true );} + bool ReadCompressed(int &var){if (DoEndianSwap()){unsigned char output[sizeof(int)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(int) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(int)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(int) * 8, true );} + bool ReadCompressed(unsigned long &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned long) * 8, true );} + bool ReadCompressed(long &var){if (DoEndianSwap()){unsigned char output[sizeof(long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(long) * 8, true );} + bool ReadCompressed(long long &var){if (DoEndianSwap()){unsigned char output[sizeof(long long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(long long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(long long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(long long) * 8, true );} + bool ReadCompressed(unsigned long long &var){if (DoEndianSwap()){unsigned char output[sizeof(unsigned long long)]; if (ReadCompressed( ( unsigned char* ) output, sizeof(unsigned long long) * 8, true )){ReverseBytes(output, (unsigned char*)&var, sizeof(unsigned long long)); return true;} return false;}else return ReadCompressed( ( unsigned char* ) & var, sizeof(unsigned long long) * 8, true );} + bool ReadCompressed(float &var){unsigned short compressedFloat; if (Read(compressedFloat)) { var = ((float)compressedFloat / 32767.5f - 1.0f); return true;} return false;} + bool ReadCompressed(double &var) {unsigned long compressedFloat; if (Read(compressedFloat)) { var = ((double)compressedFloat / 2147483648.0 - 1.0); return true; } return false;} + bool ReadCompressed(long double &var) {unsigned long compressedFloat; if (Read(compressedFloat)) { var = ((long double)compressedFloat / 2147483648.0 - 1.0); return true; } return false;} + bool ReadCompressed(SystemAddress &var) {return Read(var);} + bool ReadCompressed(NetworkID &var) {return Read(var);} + + /// Read any integral type from a bitstream. If the written value differed from the value compared against in the write function, + /// var will be updated. Otherwise it will retain the current value. + /// the current value will be updated. + /// For floating point, this is lossy, using 2 bytes for a float and 4 for a double. The range must be between -1 and +1. + /// For non-floating point, this is lossless, but only has benefit if you use less than half the range of the type + /// If you are not using __BITSTREAM_NATIVE_END the opposite is true for types larger than 1 byte + /// ReadCompressedDelta is only valid from a previous call to WriteDelta + /// \param[in] var The value to read + bool ReadCompressedDelta(bool &var) {return Read(var);} + bool ReadCompressedDelta(unsigned char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(char &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(unsigned short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(short &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(unsigned int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(int &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(unsigned long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(unsigned long long &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(float &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + bool ReadCompressedDelta(long double &var){bool dataWritten; bool success; success=Read(dataWritten); if (dataWritten) success=ReadCompressed(var); return success;} + + /// Write an array or casted stream or raw data. This does NOT do endian swapping. + /// \param[in] input a byte buffer + /// \param[in] numberOfBytes the size of \a input in bytes + void Write( const char* input, const int numberOfBytes ); + + /// Write one bitstream to another + /// \param[in] numberOfBits bits to write + /// \param bitStream the bitstream to copy from + void Write( BitStream *bitStream, int numberOfBits ); + void Write( BitStream *bitStream ); + + /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + void WriteNormVector( float x, float y, float z ); + void WriteNormVector( double x, double y, double z ) {WriteNormVector((float)x,(float)y,(float)z);} + + /// Write a vector, using 10 bytes instead of 12. + /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + void WriteVector( float x, float y, float z ); + void WriteVector( double x, double y, double z ) {WriteVector((float)x, (float)y, (float)z);} + + /// Write a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. Slightly lossy. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + void WriteNormQuat( float w, float x, float y, float z); + void WriteNormQuat( double w, double x, double y, double z) {WriteNormQuat((float)w, (float) x, (float) y, (float) z);} + + /// Write an orthogonal matrix by creating a quaternion, and writing 3 components of the quaternion in 2 bytes each + /// for 6 bytes instead of 36 + /// Lossy, although the result is renormalized + void WriteOrthMatrix( + float m00, float m01, float m02, + float m10, float m11, float m12, + float m20, float m21, float m22 ) + { + WriteOrthMatrix((double)m00,(double)m01,(double)m02, + (double)m10,(double)m11,(double)m12, + (double)m20,(double)m21,(double)m22); + } + + void WriteOrthMatrix( + double m00, double m01, double m02, + double m10, double m11, double m12, + double m20, double m21, double m22 ); + + /// Read an array or casted stream of byte. The array + /// is raw data. There is no automatic endian conversion with this function + /// \param[in] output The result byte array. It should be larger than @em numberOfBytes. + /// \param[in] numberOfBytes The number of byte to read + /// \return true on success false if there is some missing bytes. + bool Read( char* output, const int numberOfBytes ); + + /// Read a normalized 3D vector, using (at most) 4 bytes + 3 bits instead of 12-24 bytes. Will further compress y or z axis aligned vectors. + /// Accurate to 1/32767.5. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + bool ReadNormVector( float &x, float &y, float &z ); + bool ReadNormVector( double &x, double &y, double &z ) {float fx, fy, fz; bool b = ReadNormVector(fx, fy, fz); x=fx; y=fy; z=fz; return b;} + + /// Read 3 floats or doubles, using 10 bytes, where those float or doubles comprise a vector + /// Loses accuracy to about 3/10ths and only saves 2 bytes, so only use if accuracy is not important. + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + bool ReadVector( float x, float y, float z ); + bool ReadVector( double &x, double &y, double &z ) {return ReadVector((float)x, (float)y, (float)z);} + + /// Read a normalized quaternion in 6 bytes + 4 bits instead of 16 bytes. + /// \param[in] w w + /// \param[in] x x + /// \param[in] y y + /// \param[in] z z + bool ReadNormQuat( float &w, float &x, float &y, float &z){double dw, dx, dy, dz; bool b=ReadNormQuat(dw, dx, dy, dz); w=(float)dw; x=(float)dx; y=(float)dy; z=(float)dz; return b;} + bool ReadNormQuat( double &w, double &x, double &y, double &z); + + /// Read an orthogonal matrix from a quaternion, reading 3 components of the quaternion in 2 bytes each and extrapolating the 4th. + /// for 6 bytes instead of 36 + /// Lossy, although the result is renormalized + bool ReadOrthMatrix( + float &m00, float &m01, float &m02, + float &m10, float &m11, float &m12, + float &m20, float &m21, float &m22 ); + bool ReadOrthMatrix( + double &m00, double &m01, double &m02, + double &m10, double &m11, double &m12, + double &m20, double &m21, double &m22 ); + + ///Sets the read pointer back to the beginning of your data. + void ResetReadPointer( void ); + + /// Sets the write pointer back to the beginning of your data. + void ResetWritePointer( void ); + + ///This is good to call when you are done with the stream to make + /// sure you didn't leave any data left over void + void AssertStreamEmpty( void ); + + /// RAKNET_DEBUG_PRINTF the bits in the stream. Great for debugging. + void PrintBits( void ) const; + + /// Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bits to ignore + void IgnoreBits( const int numberOfBits ); + + /// Ignore data we don't intend to read + /// \param[in] numberOfBits The number of bytes to ignore + void IgnoreBytes( const int numberOfBytes ); + + ///Move the write pointer to a position on the array. + /// \param[in] offset the offset from the start of the array. + /// \attention + /// Dangerous if you don't know what you are doing! + /// For efficiency reasons you can only write mid-stream if your data is byte aligned. + void SetWriteOffset( const int offset ); + + /// Returns the length in bits of the stream + inline int GetNumberOfBitsUsed( void ) const {return GetWriteOffset();} + inline int GetWriteOffset( void ) const {return numberOfBitsUsed;} + + ///Returns the length in bytes of the stream + inline int GetNumberOfBytesUsed( void ) const {return BITS_TO_BYTES( numberOfBitsUsed );} + + ///Returns the number of bits into the stream that we have read + inline int GetReadOffset( void ) const {return readOffset;} + + // Sets the read bit index + inline void SetReadOffset( int newReadOffset ) {readOffset=newReadOffset;} + + ///Returns the number of bits left in the stream that haven't been read + inline int GetNumberOfUnreadBits( void ) const {return numberOfBitsUsed - readOffset;} + + /// Makes a copy of the internal data for you \a _data will point to + /// the stream. Returns the length in bits of the stream. Partial + /// bytes are left aligned + /// \param[out] _data The allocated copy of GetData() + int CopyData( unsigned char** _data ) const; + + /// Set the stream to some initial data. + /// \internal + void SetData( unsigned char *input ); + + /// Gets the data that BitStream is writing to / reading from + /// Partial bytes are left aligned. + /// \return A pointer to the internal state + inline unsigned char* GetData( void ) const {return data;} + + /// Write numberToWrite bits from the input source Right aligned + /// data means in the case of a partial byte, the bits are aligned + /// from the right (bit 0) rather than the left (as in the normal + /// internal representation) You would set this to true when + /// writing user data, and false when copying bitstream data, such + /// as writing one bitstream to another + /// \param[in] input The data + /// \param[in] numberOfBitsToWrite The number of bits to write + /// \param[in] rightAlignedBits if true data will be right aligned + void WriteBits( const unsigned char* input, int numberOfBitsToWrite, const bool rightAlignedBits = true ); + + /// Align the bitstream to the byte boundary and then write the + /// specified number of bits. This is faster than WriteBits but + /// wastes the bits to do the alignment and requires you to call + /// ReadAlignedBits at the corresponding read position. + /// \param[in] input The data + /// \param[in] numberOfBytesToWrite The size of input. + void WriteAlignedBytes( void *input, const int numberOfBytesToWrite ); + + /// Aligns the bitstream, writes inputLength, and writes input. Won't write beyond maxBytesToWrite + /// \param[in] input The data + /// \param[in] inputLength The size of input. + /// \param[in] maxBytesToWrite Max bytes to write + void WriteAlignedBytesSafe( void *input, const int inputLength, const int maxBytesToWrite ); + + /// Read bits, starting at the next aligned bits. Note that the + /// modulus 8 starting offset of the sequence must be the same as + /// was used with WriteBits. This will be a problem with packet + /// coalescence unless you byte align the coalesced packets. + /// \param[in] output The byte array larger than @em numberOfBytesToRead + /// \param[in] numberOfBytesToRead The number of byte to read from the internal state + /// \return true if there is enough byte. + bool ReadAlignedBytes( void *output, const int numberOfBytesToRead ); + + /// Reads what was written by WriteAlignedBytesSafe + /// \param[in] input The data + /// \param[in] maxBytesToRead Maximum number of bytes to read + bool ReadAlignedBytesSafe( void *input, int &inputLength, const int maxBytesToRead ); + + /// Same as ReadAlignedBytesSafe() but allocates the memory for you using new, rather than assuming it is safe to write to + /// \param[in] input input will be deleted if it is not a pointer to 0 + bool ReadAlignedBytesSafeAlloc( char **input, int &inputLength, const int maxBytesToRead ); + + /// Align the next write and/or read to a byte boundary. This can + /// be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + void AlignWriteToByteBoundary( void ); + + /// Align the next write and/or read to a byte boundary. This can + /// be used to 'waste' bits to byte align for efficiency reasons It + /// can also be used to force coalesced bitstreams to start on byte + /// boundaries so so WriteAlignedBits and ReadAlignedBits both + /// calculate the same offset when aligning. + void AlignReadToByteBoundary( void ); + + /// Read \a numberOfBitsToRead bits to the output source + /// alignBitsToRight should be set to true to convert internal + /// bitstream data to userdata. It should be false if you used + /// WriteBits with rightAlignedBits false + /// \param[in] output The resulting bits array + /// \param[in] numberOfBitsToRead The number of bits to read + /// \param[in] alignBitsToRight if true bits will be right aligned. + /// \return true if there is enough bits to read + bool ReadBits( unsigned char *output, int numberOfBitsToRead, const bool alignBitsToRight = true ); + + /// Write a 0 + void Write0( void ); + + /// Write a 1 + void Write1( void ); + + /// Reads 1 bit and returns true if that bit is 1 and false if it is 0 + bool ReadBit( void ); + + /// If we used the constructor version with copy data off, this + /// *makes sure it is set to on and the data pointed to is copied. + void AssertCopyData( void ); + + /// Use this if you pass a pointer copy to the constructor + /// *(_copyData==false) and want to overallocate to prevent + /// *reallocation + void SetNumberOfBitsAllocated( const unsigned int lengthInBits ); + + /// Reallocates (if necessary) in preparation of writing numberOfBitsToWrite + void AddBitsAndReallocate( const int numberOfBitsToWrite ); + + /// \internal + /// \return How many bits have been allocated internally + unsigned int GetNumberOfBitsAllocated(void) const; + + static bool DoEndianSwap(void); + static bool IsBigEndian(void); + static bool IsNetworkOrder(void); + static void ReverseBytes(unsigned char *input, unsigned char *output, int length); + static void ReverseBytesInPlace(unsigned char *data, int length); + + private: + + BitStream( const BitStream &invalid) { + #ifdef _MSC_VER + #pragma warning(disable:4100) + // warning C4100: 'invalid' : unreferenced formal parameter + #endif + + } + + /// Assume the input source points to a native type, compress and write it. + void WriteCompressed( const unsigned char* input, const int size, const bool unsignedData ); + + /// Assume the input source points to a compressed native type. Decompress and read it. + bool ReadCompressed( unsigned char* output, const int size, const bool unsignedData ); + + + int numberOfBitsUsed; + + int numberOfBitsAllocated; + + int readOffset; + + unsigned char *data; + + /// true if the internal buffer is copy of the data passed to the constructor + bool copyData; + + /// BitStreams that use less than BITSTREAM_STACK_ALLOCATION_SIZE use the stack, rather than the heap to store data. It switches over if BITSTREAM_STACK_ALLOCATION_SIZE is exceeded + unsigned char stackData[BITSTREAM_STACK_ALLOCATION_SIZE]; + }; + + inline bool BitStream::SerializeBits(bool writeToBitstream, unsigned char* input, int numberOfBitsToSerialize, const bool rightAlignedBits ) + { + if (writeToBitstream) + WriteBits(input,numberOfBitsToSerialize,rightAlignedBits); + else + return ReadBits(input,numberOfBitsToSerialize,rightAlignedBits); + return true; + } + + +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif // VC6 + +#endif \ No newline at end of file diff --git a/Multiplayer/raknet/CheckSum.h b/Multiplayer/raknet/CheckSum.h new file mode 100644 index 000000000..bdbb797c0 --- /dev/null +++ b/Multiplayer/raknet/CheckSum.h @@ -0,0 +1,54 @@ +/// \file +/// \brief \b [Internal] Generates and validates checksums +/// +/// \note I didn't write this, but took it from http://www.flounder.com/checksum.htm +/// + +#ifndef __CHECKSUM_H +#define __CHECKSUM_H + +#include "RakMemoryOverride.h" + +/// Generates and validates checksums +class CheckSum +{ + +public: + + /// 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 diff --git a/Multiplayer/raknet/ClientContextStruct.h b/Multiplayer/raknet/ClientContextStruct.h new file mode 100644 index 000000000..b3ada2be7 --- /dev/null +++ b/Multiplayer/raknet/ClientContextStruct.h @@ -0,0 +1,50 @@ +/// \file +/// \brief \b [Internal] Depreciated, back from when I supported IO Completion ports. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __CLIENT_CONTEXT_STRUCT_H +#define __CLIENT_CONTEXT_STRUCT_H + +#if defined(_XBOX) || defined(X360) +#elif defined(_WIN32) +//#include +#endif +#include "RakNetTypes.h" +#include "MTUSize.h" + +class RakPeer; + +#ifdef __USE_IO_COMPLETION_PORTS + +struct ClientContextStruct +{ + HANDLE handle; // The socket, also used as a file handle +}; + +struct ExtendedOverlappedStruct +{ + OVERLAPPED overlapped; + char data[ MAXIMUM_MTU_SIZE ]; // Used to hold data to send + int length; // Length of the actual data to send, always under MAXIMUM_MTU_SIZE + unsigned int binaryAddress; + unsigned short port; + RakPeer *rakPeer; + bool read; // Set to true for reads, false for writes +}; + +#endif + +#endif diff --git a/Multiplayer/raknet/CommandParserInterface.h b/Multiplayer/raknet/CommandParserInterface.h new file mode 100644 index 000000000..8d004c68f --- /dev/null +++ b/Multiplayer/raknet/CommandParserInterface.h @@ -0,0 +1,149 @@ +/// \file +/// \brief Contains CommandParserInterface , from which you derive custom command parsers +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __COMMAND_PARSER_INTERFACE +#define __COMMAND_PARSER_INTERFACE + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_OrderedList.h" +#include "Export.h" + +class TransportInterface; + +/// \internal +/// Contains the information related to one command registered with RegisterCommand() +/// Implemented so I can have an automatic help system via SendCommandList() +struct RAK_DLL_EXPORT RegisteredCommand +{ + const char *command; + const char *commandHelp; + unsigned char parameterCount; +}; + +/// List of commands registered with RegisterCommand() +int RAK_DLL_EXPORT RegisteredCommandComp( const char* const & key, const RegisteredCommand &data ); + +/// CommandParserInterface provides a set of functions and interfaces that plug into the ConsoleServer class. +/// Each CommandParserInterface works at the same time as other interfaces in the system. +/// \brief The interface used by command parsers. +class RAK_DLL_EXPORT CommandParserInterface +{ +public: + CommandParserInterface(); + virtual ~CommandParserInterface(); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + virtual const char *GetName(void) const=0; + + /// A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + virtual void OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport); + + /// A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + virtual void OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport); + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + virtual void SendHelp(TransportInterface *transport, SystemAddress systemAddress)=0; + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + virtual bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString)=0; + + /// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + virtual void OnTransportChange(TransportInterface *transport); + + /// \internal + /// Scan commandList and return the associated array + /// \param[in] command The string to find + /// \param[out] rc Contains the result of this operation + /// \return True if we found the command, false otherwise + virtual bool GetRegisteredCommand(const char *command, RegisteredCommand *rc); + + /// \internal + /// Goes through str, replacing the delineating character with 0's. + /// \param[in] str The string sent by the transport interface + /// \param[in] delineator The character to scan for to use as a delineator + /// \param[in] delineatorToggle When encountered the delineator replacement is toggled on and off + /// \param[out] numParameters How many pointers were written to \a parameterList + /// \param[out] parameterList An array of pointers to characters. Will hold pointers to locations inside \a str + /// \param[in] parameterListLength How big the \a parameterList array is + static void ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength); + + /// \internal + /// Goes through the variable commandList and sends the command portion of each struct + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player to write to + virtual void SendCommandList(TransportInterface *transport, SystemAddress systemAddress); + + static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS; + +protected: + // Currently only takes static strings - doesn't make a copy of what you pass. + // parameterCount is the number of parameters that the sender has to include with the command. + // Pass 255 to parameterCount to indicate variable number of parameters + + /// Registers a command. + /// \param[in] parameterCount How many parameters your command requires. If you want to accept a variable number of commands, pass CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS + /// \param[in] command A pointer to a STATIC string that has your command. I keep a copy of the pointer here so don't deallocate the string. + /// \param[in] commandHelp A pointer to a STATIC string that has the help information for your command. I keep a copy of the pointer here so don't deallocate the string. + virtual void RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp); + + /// Just writes a string to the remote system based on the result ( \a res )of your operation + /// This is not necessary to call, but makes it easier to return results of function calls + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(bool res, const char *command, TransportInterface *transport, SystemAddress systemAddress); + virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, SystemAddress systemAddress); + virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, SystemAddress systemAddress); + virtual void ReturnResult(int res, const char *command,TransportInterface *transport, SystemAddress systemAddress); + + /// Just writes a string to the remote system when you are calling a function that has no return value + /// This is not necessary to call, but makes it easier to return results of function calls + /// \param[in] res The result to write + /// \param[in] command The command that this result came from + /// \param[in] transport The transport interface that will be written to + /// \param[in] systemAddress The player this result will be sent to + virtual void ReturnResult(const char *command,TransportInterface *transport, SystemAddress systemAddress); + + + /// Since there's no way to specify a systemAddress directly, the user needs to specify both the binary address and port. + /// Given those parameters, this returns the corresponding SystemAddress + /// \param[in] binaryAddress The binaryAddress portion of SystemAddress + /// \param[in] port The port portion of SystemAddress + SystemAddress IntegersToSystemAddress(int binaryAddress, int port); + + DataStructures::OrderedList commandList; +}; + +#endif diff --git a/Multiplayer/raknet/ConnectionGraph.h b/Multiplayer/raknet/ConnectionGraph.h new file mode 100644 index 000000000..c14679623 --- /dev/null +++ b/Multiplayer/raknet/ConnectionGraph.h @@ -0,0 +1,168 @@ +/// \file +/// \brief Connection graph plugin. This maintains a graph of connections for the entire network, so every peer knows about every other peer. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __CONNECTION_GRAPH_H +#define __CONNECTION_GRAPH_H + +class RakPeerInterface; +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "PluginInterface.h" +#include "DS_List.h" +#include "DS_WeightedGraph.h" +#include "GetTime.h" +#include "Export.h" + +// If you need more than 255 groups just change this typedef +typedef unsigned char ConnectionGraphGroupID; + +/// \defgroup CONNECTION_GRAPH_GROUP ConnectionGraph +/// \ingroup PLUGINS_GROUP + +/// \ingroup CONNECTION_GRAPH_GROUP +/// \brief A connection graph. Each peer will know about all other peers. +class RAK_DLL_EXPORT ConnectionGraph : public PluginInterface +{ +public: + ConnectionGraph(); + virtual ~ConnectionGraph(); + + /// A node in the connection graph + struct RAK_DLL_EXPORT SystemAddressAndGroupId + { + SystemAddressAndGroupId(); + ~SystemAddressAndGroupId(); + SystemAddressAndGroupId(SystemAddress _systemAddress, ConnectionGraphGroupID _groupID, RakNetGUID _guid); + + SystemAddress systemAddress; + ConnectionGraphGroupID groupId; + RakNetGUID guid; + + bool operator==( const SystemAddressAndGroupId& right ) const; + bool operator!=( const SystemAddressAndGroupId& right ) const; + bool operator > ( const SystemAddressAndGroupId& right ) const; + bool operator < ( const SystemAddressAndGroupId& right ) const; + }; + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// Plaintext encoding of the password, or 0 for none. If you use a password, use secure connections + void SetPassword(const char *password); + + /// Returns the connection graph + /// \return The connection graph, stored as map of adjacency lists + DataStructures::WeightedGraph *GetGraph(void); + + /// Automatically add new connections to the connection graph + /// Defaults to true + /// If true, then the system will automatically add all new connections for you, assigning groupId 0 to these systems. + /// If you want more control, you should call SetAutoAddNewConnections(false); + /// When false, it is up to you to call RequestConnectionGraph and AddNewConnection to complete the graph + /// However, this way you can choose which nodes are on the graph for this system and can assign groupIds to those nodes + /// \param[in] autoAdd true to automatically add new connections to the connection graph. False to not do so. + void SetAutoAddNewConnections(bool autoAdd); + + /// Requests the connection graph from another system + /// Only necessary to call if SetAutoAddNewConnections(false) is called. + /// You should call this sometime after getting ID_CONNECTION_REQUEST_ACCEPTED and \a systemAddress is or should be a node on the connection graph + /// \param[in] peer The instance of RakPeer to send through + /// \param[in] systemAddress The system to send to + void RequestConnectionGraph(RakPeerInterface *peer, SystemAddress systemAddress); + + /// Adds a new connection to the connection graph from this system to the specified system. Also assigns a group identifier for that system + /// Only used and valid when SetAutoAddNewConnections(false) is called. + /// Call this for this system sometime after getting ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED for systems that contain a connection graph + /// Groups are sets of one or more nodes in the total graph + /// We only add to the graph groups which we subscribe to + /// \param[in] peer The instance of RakPeer to send through + /// \param[in] systemAddress The system that is connected to us. + /// \param[in] guid The system that is connected to us. + /// \param[in] groupId Just a number representing a group. Important: 0 is reserved to mean unassigned group ID and is assigned to all systems added with SetAutoAddNewConnections(true) + void AddNewConnection(RakPeerInterface *peer, SystemAddress systemAddress, RakNetGUID guid, ConnectionGraphGroupID groupId); + + /// Sets our own group ID + /// Only used and valid when SetAutoAddNewConnections(false) is called. + /// Defaults to 0 + /// \param[in] groupId Our group ID + void SetGroupId(ConnectionGraphGroupID groupId); + + /// Allows adding to the connection graph nodes with this groupId. + /// By default, you subscribe to group 0, which are all systems automatically added with SetAutoAddNewConnections(true) + /// Calling this does not add nodes which were previously rejected due to an unsubscribed group - it only allows addition of nodes after the fact + /// \param[in] groupId Just a number representing a group. 0 is reserved to mean unassigned group ID, automatically added with SetAutoAddNewConnections(true) + void SubscribeToGroup(ConnectionGraphGroupID groupId); + + /// Disables addition of graph nodes with this groupId + /// Calling this does not add remove nodes with this groupId which are already present in the graph. It only disables addition of nodes after the fact + /// \param[in] groupId Just a number representing a group. 0 is reserved to mean unassigned group ID, automatically added with SetAutoAddNewConnections(true) + void UnsubscribeFromGroup(ConnectionGraphGroupID groupId); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + /// \internal + virtual void OnShutdown(RakPeerInterface *peer); + /// \internal + virtual void Update(RakPeerInterface *peer); + /// \internal + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + /// \internal + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + +protected: + void HandleDroppedConnection(RakPeerInterface *peer, SystemAddress systemAddress, unsigned char packetId); + void DeleteFromPeerList(SystemAddress systemAddress); + + void OnNewIncomingConnection(RakPeerInterface *peer, Packet *packet); + void OnConnectionRequestAccepted(RakPeerInterface *peer, Packet *packet); + void OnConnectionGraphRequest(RakPeerInterface *peer, Packet *packet); + void OnConnectionGraphReply(RakPeerInterface *peer, Packet *packet); + void OnConnectionGraphUpdate(RakPeerInterface *peer, Packet *packet); + void OnNewConnection(RakPeerInterface *peer, Packet *packet); + bool OnConnectionLost(RakPeerInterface *peer, Packet *packet, unsigned char packetId); + void OnConnectionAddition(RakPeerInterface *peer, Packet *packet); + void OnConnectionRemoval(RakPeerInterface *peer, Packet *packet); + void SendConnectionGraph(SystemAddress target, unsigned char packetId, RakPeerInterface *peer); + void SerializeWeightedGraph(RakNet::BitStream *out, const DataStructures::WeightedGraph &g) const; + bool DeserializeWeightedGraph(RakNet::BitStream *inBitstream, RakPeerInterface *peer); + void AddAndRelayConnection(DataStructures::OrderedList &ignoreList, const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2, unsigned short ping, RakPeerInterface *peer); + bool RemoveAndRelayConnection(DataStructures::OrderedList &ignoreList, unsigned char packetId, const SystemAddress node1, const SystemAddress node2, RakPeerInterface *peer); + void RemoveParticipant(SystemAddress systemAddress); + void AddParticipant(SystemAddress systemAddress); + void BroadcastGraphUpdate(DataStructures::OrderedList &ignoreList, RakPeerInterface *peer); + void NotifyUserOfRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,unsigned short ping, RakPeerInterface *peer); + bool IsNewRemoteConnection(const SystemAddressAndGroupId &conn1, const SystemAddressAndGroupId &conn2,RakPeerInterface *peer); + bool DeserializeIgnoreList(DataStructures::OrderedList &ignoreList, RakNet::BitStream *inBitstream ); + void SerializeIgnoreListAndBroadcast(RakNet::BitStream *outBitstream, DataStructures::OrderedList &ignoreList, RakPeerInterface *peer); + + RakNetTime nextWeightUpdate; + char *pw; + DataStructures::OrderedList participantList; + + DataStructures::WeightedGraph graph; + bool autoAddNewConnections; + ConnectionGraphGroupID myGroupId; + + DataStructures::OrderedList subscribedGroups; + + // Used to broadcast new connections after some time so the pings are correct + //RakNetTime forceBroadcastTime; + +}; + +#endif diff --git a/Multiplayer/raknet/ConsoleServer.h b/Multiplayer/raknet/ConsoleServer.h new file mode 100644 index 000000000..04c983183 --- /dev/null +++ b/Multiplayer/raknet/ConsoleServer.h @@ -0,0 +1,63 @@ +/// \file +/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __CONSOLE_SERVER_H +#define __CONSOLE_SERVER_H + +class TransportInterface; +class CommandParserInterface; + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" + +/// \brief The main entry point for the server portion of your remote console application support. +/// ConsoleServer takes one TransportInterface and one or more CommandParserInterface (s) +/// The TransportInterface will be used to send data between the server and the client. The connecting client must support the +/// protocol used by your derivation of TransportInterface . TelnetTransport and RakNetTransport are two such derivations . +/// When a command is sent by a remote console, it will be processed by your implementations of CommandParserInterface +class RAK_DLL_EXPORT ConsoleServer +{ +public: + ConsoleServer(); + ~ConsoleServer(); + + /// Call this with a derivation of TransportInterface so that the console server can send and receive commands + /// \param[in] transportInterface Your interface to use. + /// \param[in] port The port to host on. Telnet uses port 23 by default. RakNet can use whatever you want. + void SetTransportProvider(TransportInterface *transportInterface, unsigned short port); + + /// Add an implementation of CommandParserInterface to the list of command parsers. + /// \param[in] commandParserInterface The command parser referred to + void AddCommandParser(CommandParserInterface *commandParserInterface); + + /// Remove an implementation of CommandParserInterface previously added with AddCommandParser() + /// \param[in] commandParserInterface The command parser referred to + void RemoveCommandParser(CommandParserInterface *commandParserInterface); + + /// Call update to read packet sent from your TransportInterface. + /// You should do this fairly frequently. + void Update(void); +protected: + void ListParsers(SystemAddress systemAddress); + TransportInterface *transport; + DataStructures::List commandParserList; + char* password[256]; +}; + +#endif diff --git a/Multiplayer/raknet/DS_BPlusTree.h b/Multiplayer/raknet/DS_BPlusTree.h new file mode 100644 index 000000000..405c250fc --- /dev/null +++ b/Multiplayer/raknet/DS_BPlusTree.h @@ -0,0 +1,1162 @@ +#ifndef __B_PLUS_TREE_CPP +#define __B_PLUS_TREE_CPP + +#include "DS_MemoryPool.h" +#include "DS_Queue.h" +#include +#include "Export.h" + +// Java +// http://www.seanster.com/BplusTree/BplusTree.html + +// Overview +// http://babbage.clarku.edu/~achou/cs160/B+Trees/B+Trees.htm + +// Deletion +// http://dbpubs.stanford.edu:8090/pub/1995-19 + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// Used in the BPlusTree. Used for both leaf and index nodes. + /// Don't use a constructor or destructor, due to the memory pool I am using + template + struct RAK_DLL_EXPORT Page + { + // We use the same data structure for both leaf and index nodes. It uses a little more memory for index nodes but reduces + // memory fragmentation, allocations, and deallocations. + bool isLeaf; + + // Used for both leaf and index nodes. + // For a leaf it means the number of elements in data + // For an index it means the number of keys and is one less than the number of children pointers. + int size; + + // Used for both leaf and index nodes. + KeyType keys[order]; + + // Used only for leaf nodes. Data is the actual data, while next is the pointer to the next leaf (for B+) + DataType data[order]; + Page *next; + Page *previous; + + // Used only for index nodes. Pointers to the children of this node. + Page *children[order+1]; + }; + + /// A BPlus tree + /// Written with efficiency and speed in mind. + template + class RAK_DLL_EXPORT BPlusTree + { + public: + struct ReturnAction + { + KeyType key1; + KeyType key2; + enum + { + NO_ACTION, + REPLACE_KEY1_WITH_KEY2, + PUSH_KEY_TO_PARENT, + SET_BRANCH_KEY, + } action; // 0=none, 1=replace key1 with key2 + }; + + BPlusTree(); + ~BPlusTree(); + void SetPoolPageSize(int size); // Set the page size for the memory pool. Optionsl + bool Get(const KeyType key, DataType &out) const; + bool Delete(const KeyType key); + bool Delete(const KeyType key, DataType &out); + bool Insert(const KeyType key, const DataType &data); + void Clear(void); + unsigned Size(void) const; + bool IsEmpty(void) const; + Page *GetListHead(void) const; + DataType GetDataHead(void) const; + void PrintLeaves(void); + void ForEachLeaf(void (*func)(Page * leaf, int index)); + void ForEachData(void (*func)(DataType input, int index)); + void PrintGraph(void); + void ValidateTree(void); + protected: + void ValidateTreeRecursive(Page *cur); + void DeleteFromPageAtIndex(const int index, Page *cur); + static void PrintLeaf(Page * leaf, int index); + void FreePages(void); + bool GetIndexOf(const KeyType key, Page *page, int *out) const; + void ShiftKeysLeft(Page *cur); + bool CanRotateLeft(Page *cur, int childIndex); + bool CanRotateRight(Page *cur, int childIndex); + void RotateRight(Page *cur, int childIndex, ReturnAction *returnAction); + void RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction); + Page* InsertIntoNode(const KeyType key, const DataType &childData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction); + Page* InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction* returnAction, bool *success); + Page* GetLeafFromKey(const KeyType key) const; + bool FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out); + bool FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction); + void ShiftNodeLeft(Page *cur); + void ShiftNodeRight(Page *cur); + + MemoryPool > pagePool; + Page *root, *leftmostLeaf; + }; + + template + BPlusTree::BPlusTree () + { + RakAssert(order>1); + root=0; + leftmostLeaf=0; + } + template + BPlusTree::~BPlusTree () + { + Clear(); + } + template + void BPlusTree::SetPoolPageSize(int size) + { + pagePool.SetPageSize(size); + } + template + bool BPlusTree::Get(const KeyType key, DataType &out) const + { + if (root==0) + return false; + + Page* leaf = GetLeafFromKey(key); + int childIndex; + + if (GetIndexOf(key, leaf, &childIndex)) + { + out=leaf->data[childIndex]; + return true; + } + return false; + } + template + void BPlusTree::DeleteFromPageAtIndex(const int index, Page *cur) + { + int i; + for (i=index; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=index; i < cur->size-1; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=index; i < cur->size-1; i++) + cur->children[i+1]=cur->children[i+2]; + } + cur->size--; + } + template + bool BPlusTree::Delete(const KeyType key) + { + DataType temp; + return Delete(key, temp); + } + template + bool BPlusTree::Delete(const KeyType key, DataType &out) + { + if (root==0) + return false; + + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + int childIndex; + bool underflow=false; + if (root==leftmostLeaf) + { + if (GetIndexOf(key, root, &childIndex)==false) + return false; + out=root->data[childIndex]; + DeleteFromPageAtIndex(childIndex,root); + if (root->size==0) + { + pagePool.Release(root); + root=0; + leftmostLeaf=0; + } + return true; + } + else if (FindDeleteRebalance(key, root, &underflow,root->keys[0], &returnAction, out)==false) + return false; + +// RakAssert(returnAction.action==ReturnAction::NO_ACTION); + + if (underflow && root->size==0) + { + // Move the root down. + Page *oldRoot=root; + root=root->children[0]; + pagePool.Release(oldRoot); + // memset(oldRoot,0,sizeof(root)); + } + + return true; + } + template + bool BPlusTree::FindDeleteRebalance(const KeyType key, Page *cur, bool *underflow, KeyType rightRootKey, ReturnAction *returnAction, DataType &out) + { + // Get index of child to follow. + int branchIndex, childIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + + // If child is not a leaf, call recursively + if (cur->children[branchIndex]->isLeaf==false) + { + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (FindDeleteRebalance(key, cur->children[branchIndex], underflow, rightRootKey, returnAction, out)==false) + return false; + + // Call again in case the root key changed + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + + if (returnAction->action==ReturnAction::SET_BRANCH_KEY && branchIndex!=childIndex) + { + returnAction->action=ReturnAction::NO_ACTION; + cur->keys[childIndex]=returnAction->key1; + + if (branchIndexsize) + rightRootKey=cur->keys[branchIndex]; // Shift right to left + else + rightRootKey=cur->keys[branchIndex-1]; // Shift center to left + } + } + else + { + // If child is a leaf, get the index of the key. If the item is not found, cancel delete. + if (GetIndexOf(key, cur->children[branchIndex], &childIndex)==false) + return false; + + // Delete: + // Remove childIndex from the child at branchIndex + out=cur->children[branchIndex]->data[childIndex]; + DeleteFromPageAtIndex(childIndex, cur->children[branchIndex]); + + if (childIndex==0) + { + if (branchIndex>0) + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=cur->children[0]->keys[0]; + } + } + + if (cur->children[branchIndex]->size < order/2) + *underflow=true; + else + *underflow=false; + } + + // Fix underflow: + if (*underflow) + { + *underflow=FixUnderflow(branchIndex, cur, rightRootKey, returnAction); + } + + return true; + } + template + bool BPlusTree::FixUnderflow(int branchIndex, Page *cur, KeyType rightRootKey, ReturnAction *returnAction) + { + // Borrow from a neighbor that has excess. + Page *source; + Page *dest; + + if (branchIndex>0 && cur->children[branchIndex-1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex-1]; + + // Left has excess + ShiftNodeRight(dest); + if (dest->isLeaf) + { + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + } + else + { + dest->children[0]=source->children[source->size]; + dest->keys[0]=cur->keys[branchIndex-1]; + } + // Update the parent key for the child (middle) + cur->keys[branchIndex-1]=source->keys[source->size-1]; + source->size--; + + // if (branchIndex==0) + // { + // returnAction->action=ReturnAction::SET_BRANCH_KEY; + // returnAction->key1=dest->keys[0]; + // } + + // No underflow + return false; + } + else if (branchIndexsize && cur->children[branchIndex+1]->size > order/2) + { + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + + // Right has excess + if (dest->isLeaf) + { + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + + // The first key in the leaf after shifting is the parent key for the right branch + cur->keys[branchIndex]=source->keys[1]; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && dest->size==0) + { + if (branchIndex==0) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + else + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + } + } + else + { + if (returnAction->action==ReturnAction::NO_ACTION) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size+1]=source->children[0]; + + // The shifted off key is the leftmost key for a node + cur->keys[branchIndex]=source->keys[0]; + } + + + dest->size++; + ShiftNodeLeft(source); + + //cur->keys[branchIndex]=source->keys[0]; + +// returnAction->action=ReturnAction::SET_BRANCH_KEY; +// returnAction->key1=dest->keys[dest->size-1]; + + // No underflow + return false; + } + else + { + int sourceIndex; + + // If no neighbors have excess, merge two branches. + // + // To merge two leaves, just copy the data and keys over. + // + // To merge two branches, copy the pointers and keys over, using rightRootKey as the key for the extra pointer + if (branchIndexsize) + { + // Merge right child to current child and delete right child. + dest=cur->children[branchIndex]; + source=cur->children[branchIndex+1]; + } + else + { + // Move current child to left and delete current child + dest=cur->children[branchIndex-1]; + source=cur->children[branchIndex]; + } + + // Merge + if (dest->isLeaf) + { + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->data[dest->size++]=source->data[sourceIndex]; + } + } + else + { + // We want the tree root key of the source, not the current. + dest->keys[dest->size]=rightRootKey; + dest->children[dest->size++ + 1]=source->children[0]; + for (sourceIndex=0; sourceIndexsize; sourceIndex++) + { + dest->keys[dest->size]=source->keys[sourceIndex]; + dest->children[dest->size++ + 1]=source->children[sourceIndex + 1]; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + if (order<=3 && branchIndex>0 && cur->children[branchIndex]->isLeaf) // With order==2 it is possible to delete data[0], which is not possible with higher orders. + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + if (branchIndexsize) + { + // Update the parent key, removing the source (right) + DeleteFromPageAtIndex(branchIndex, cur); + } + else + { + if (branchIndex>0) + { + // Update parent key, removing the source (current) + DeleteFromPageAtIndex(branchIndex-1, cur); + } + } + + if (branchIndex==0 && dest->isLeaf) + { + returnAction->action=ReturnAction::SET_BRANCH_KEY; + returnAction->key1=dest->keys[0]; + } + + if (source==leftmostLeaf) + leftmostLeaf=source->next; + + if (source->isLeaf) + { + if (source->previous) + source->previous->next=source->next; + if (source->next) + source->next->previous=source->previous; + } + + // Free the source node + pagePool.Release(source); + // memset(source,0,sizeof(root)); + + // Return underflow or not of parent. + return cur->size < order/2; + } + } + template + void BPlusTree::ShiftNodeRight(Page *cur) + { + int i; + for (i=cur->size; i>0; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i>0; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i>0; i--) + cur->children[i]=cur->children[i-1]; + } + + cur->size++; + } + template + void BPlusTree::ShiftNodeLeft(Page *cur) + { + int i; + for (i=0; i < cur->size-1; i++) + cur->keys[i]=cur->keys[i+1]; + if (cur->isLeaf) + { + for (i=0; i < cur->size; i++) + cur->data[i]=cur->data[i+1]; + } + else + { + for (i=0; i < cur->size; i++) + cur->children[i]=cur->children[i+1]; + } + cur->size--; + } + template + Page* BPlusTree::InsertIntoNode(const KeyType key, const DataType &leafData, int insertionIndex, Page *nodeData, Page *cur, ReturnAction* returnAction) + { + int i; + if (cur->size < order) + { + for (i=cur->size; i > insertionIndex; i--) + cur->keys[i]=cur->keys[i-1]; + if (cur->isLeaf) + { + for (i=cur->size; i > insertionIndex; i--) + cur->data[i]=cur->data[i-1]; + } + else + { + for (i=cur->size+1; i > insertionIndex+1; i--) + cur->children[i]=cur->children[i-1]; + } + cur->keys[insertionIndex]=key; + if (cur->isLeaf) + cur->data[insertionIndex]=leafData; + else + cur->children[insertionIndex+1]=nodeData; + + cur->size++; + } + else + { + Page* newPage = pagePool.Allocate(); + newPage->isLeaf=cur->isLeaf; + if (cur->isLeaf) + { + newPage->next=cur->next; + if (cur->next) + cur->next->previous=newPage; + newPage->previous=cur; + cur->next=newPage; + } + + int destIndex, sourceIndex; + + if (insertionIndex>=(order+1)/2) + { + destIndex=0; + sourceIndex=order/2; + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + newPage->keys[destIndex++]=key; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->keys[destIndex]=cur->keys[sourceIndex]; + } + + destIndex=0; + sourceIndex=order/2; + if (cur->isLeaf) + { + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + newPage->data[destIndex++]=leafData; + for (; sourceIndex < order; sourceIndex++, destIndex++) + { + newPage->data[destIndex]=cur->data[sourceIndex]; + } + } + else + { + + for (; sourceIndex < insertionIndex; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + newPage->children[destIndex++]=nodeData; + + // sourceIndex+1 is sort of a hack but it works - because there is one extra child than keys + // skip past the last child for cur + for (; sourceIndex+1 < cur->size+1; sourceIndex++, destIndex++) + { + newPage->children[destIndex]=cur->children[sourceIndex+1]; + } + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + + } + cur->size=order/2; + } + else + { + destIndex=0; + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->keys[destIndex]=cur->keys[sourceIndex]; + destIndex=0; + if (cur->isLeaf) + { + sourceIndex=(order+1)/2-1; + for (; sourceIndex < order; sourceIndex++, destIndex++) + newPage->data[destIndex]=cur->data[sourceIndex]; + } + else + { + sourceIndex=(order+1)/2; + for (; sourceIndex < order+1; sourceIndex++, destIndex++) + newPage->children[destIndex]=cur->children[sourceIndex]; + + // the first key is the middle key. Remove it from the page and push it to the parent + returnAction->action=ReturnAction::PUSH_KEY_TO_PARENT; + returnAction->key1=newPage->keys[0]; + for (int i=0; i < destIndex-1; i++) + newPage->keys[i]=newPage->keys[i+1]; + } + cur->size=(order+1)/2-1; + if (cur->size) + { + bool b = GetIndexOf(key, cur, &insertionIndex); + (void) b; + RakAssert(b==false); + } + else + insertionIndex=0; + InsertIntoNode(key, leafData, insertionIndex, nodeData, cur, returnAction); + } + + newPage->size=destIndex; + + return newPage; + } + + return 0; + } + + template + bool BPlusTree::CanRotateLeft(Page *cur, int childIndex) + { + return childIndex>0 && cur->children[childIndex-1]->size + void BPlusTree::RotateLeft(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex-1]; + Page *source = cur->children[childIndex]; + returnAction->key1=source->keys[0]; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + for (int i=0; i < source->size-1; i++) + { + source->keys[i]=source->keys[i+1]; + source->data[i]=source->data[i+1]; + } + source->size--; + cur->keys[childIndex-1]=source->keys[0]; + returnAction->key2=source->keys[0]; + } + + template + bool BPlusTree::CanRotateRight(Page *cur, int childIndex) + { + return childIndex < cur->size && cur->children[childIndex+1]->size + void BPlusTree::RotateRight(Page *cur, int childIndex, ReturnAction *returnAction) + { + Page *dest = cur->children[childIndex+1]; + Page *source = cur->children[childIndex]; + returnAction->key1=dest->keys[0]; + for (int i= dest->size; i > 0; i--) + { + dest->keys[i]=dest->keys[i-1]; + dest->data[i]=dest->data[i-1]; + } + dest->keys[0]=source->keys[source->size-1]; + dest->data[0]=source->data[source->size-1]; + dest->size++; + source->size--; + + cur->keys[childIndex]=dest->keys[0]; + returnAction->key2=dest->keys[0]; + } + template + Page* BPlusTree::GetLeafFromKey(const KeyType key) const + { + Page* cur = root; + int childIndex; + while (cur->isLeaf==false) + { + // When searching, if we match the exact key we go down the pointer after that index + if (GetIndexOf(key, cur, &childIndex)) + childIndex++; + cur = cur->children[childIndex]; + } + return cur; + } + + template + Page* BPlusTree::InsertBranchDown(const KeyType key, const DataType &data,Page *cur, ReturnAction *returnAction, bool *success) + { + int childIndex; + int branchIndex; + if (GetIndexOf(key, cur, &childIndex)) + branchIndex=childIndex+1; + else + branchIndex=childIndex; + Page* newPage; + if (cur->isLeaf==false) + { + if (cur->children[branchIndex]->isLeaf==true && cur->children[branchIndex]->size==order) + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + + if (CanRotateLeft(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + if (key > cur->children[branchIndex]->keys[0]) + { + RotateLeft(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + } + else + { + // Move head element to left and replace it with key,data + Page* dest=cur->children[branchIndex-1]; + Page* source=cur->children[branchIndex]; + returnAction->key1=source->keys[0]; + returnAction->key2=key; + dest->keys[dest->size]=source->keys[0]; + dest->data[dest->size]=source->data[0]; + dest->size++; + source->keys[0]=key; + source->data[0]=data; + } + cur->keys[branchIndex-1]=cur->children[branchIndex]->keys[0]; + + return 0; + } + else if (CanRotateRight(cur, branchIndex)) + { + returnAction->action=ReturnAction::REPLACE_KEY1_WITH_KEY2; + + if (key < cur->children[branchIndex]->keys[cur->children[branchIndex]->size-1]) + { + RotateRight(cur, branchIndex, returnAction); + + int insertionIndex; + GetIndexOf(key, cur->children[branchIndex], &insertionIndex); + InsertIntoNode(key, data, insertionIndex, 0, cur->children[branchIndex], 0); + + } + else + { + // Insert to the head of the right leaf instead and change our key + returnAction->key1=cur->children[branchIndex+1]->keys[0]; + InsertIntoNode(key, data, 0, 0, cur->children[branchIndex+1], 0); + returnAction->key2=key; + } + cur->keys[branchIndex]=cur->children[branchIndex+1]->keys[0]; + return 0; + } + } + + newPage=InsertBranchDown(key,data,cur->children[branchIndex], returnAction, success); + if (returnAction->action==ReturnAction::REPLACE_KEY1_WITH_KEY2) + { + if (branchIndex>0 && cur->keys[branchIndex-1]==returnAction->key1) + cur->keys[branchIndex-1]=returnAction->key2; + } + if (newPage) + { + if (newPage->isLeaf==false) + { + RakAssert(returnAction->action==ReturnAction::PUSH_KEY_TO_PARENT); + newPage->size--; + return InsertIntoNode(returnAction->key1, data, branchIndex, newPage, cur, returnAction); + } + else + { + return InsertIntoNode(newPage->keys[0], data, branchIndex, newPage, cur, returnAction); + } + } + } + else + { + if (branchIndex==childIndex+1) + { + *success=false; + return 0; // Already exists + } + else + { + return InsertIntoNode(key, data, branchIndex, 0, cur, returnAction); + } + } + + return 0; + } + template + bool BPlusTree::Insert(const KeyType key, const DataType &data) + { + if (root==0) + { + // Allocate root and make root a leaf + root = pagePool.Allocate(); + root->isLeaf=true; + leftmostLeaf=root; + root->size=1; + root->keys[0]=key; + root->data[0]=data; + root->next=0; + root->previous=0; + } + else + { + bool success=true; + ReturnAction returnAction; + returnAction.action=ReturnAction::NO_ACTION; + Page* newPage = InsertBranchDown(key, data, root, &returnAction, &success); + if (success==false) + return false; + if (newPage) + { + KeyType newKey; + if (newPage->isLeaf==false) + { + // One key is pushed up through the stack. I store that at keys[0] but it has to be removed for the page to be correct + RakAssert(returnAction.action==ReturnAction::PUSH_KEY_TO_PARENT); + newKey=returnAction.key1; + newPage->size--; + } + else + newKey = newPage->keys[0]; + // propagate the root + Page* newRoot = pagePool.Allocate(); + newRoot->isLeaf=false; + newRoot->size=1; + newRoot->keys[0]=newKey; + newRoot->children[0]=root; + newRoot->children[1]=newPage; + root=newRoot; + } + } + + return true; + } + template + void BPlusTree::ShiftKeysLeft(Page *cur) + { + int i; + for (i=0; i < cur->size; i++) + cur->keys[i]=cur->keys[i+1]; + } + template + void BPlusTree::Clear(void) + { + if (root) + { + FreePages(); + leftmostLeaf=0; + root=0; + } + pagePool.Clear(); + } + template + unsigned BPlusTree::Size(void) const + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + count+=cur->size; + cur=cur->next; + } + return count; + } + template + bool BPlusTree::IsEmpty(void) const + { + return root==0; + } + template + bool BPlusTree::GetIndexOf(const KeyType key, Page *page, int *out) const + { + RakAssert(page->size>0); + int index, upperBound, lowerBound; + upperBound=page->size-1; + lowerBound=0; + index = page->size/2; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + if (key==page->keys[index]) + { + *out=index; + return true; + } + else if (keykeys[index]) + upperBound=index-1; + else + lowerBound=index+1; + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *out=lowerBound; + return false; // No match + } + } + } + template + void BPlusTree::FreePages(void) + { + DataStructures::Queue *> queue; + DataStructures::Page *ptr; + int i; + queue.Push(root); + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + queue.Push(ptr->children[i]); + } + pagePool.Release(ptr); + // memset(ptr,0,sizeof(root)); + }; + } + template + Page *BPlusTree::GetListHead(void) const + { + return leftmostLeaf; + } + template + DataType BPlusTree::GetDataHead(void) const + { + return leftmostLeaf->data[0]; + } + template + void BPlusTree::ForEachLeaf(void (*func)(Page * leaf, int index)) + { + int count=0; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + func(cur, count++); + cur=cur->next; + } + } + template + void BPlusTree::ForEachData(void (*func)(DataType input, int index)) + { + int count=0,i; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + for (i=0; i < cur->size; i++) + func(cur->data[i], count++); + cur=cur->next; + } + } + template + void BPlusTree::PrintLeaf(Page * leaf, int index) + { + int i; + RAKNET_DEBUG_PRINTF("%i] SELF=%p\n", index+1, leaf); + for (i=0; i < leaf->size; i++) + RAKNET_DEBUG_PRINTF(" %i. %i\n", i+1, leaf->data[i]); + } + template + void BPlusTree::PrintLeaves(void) + { + ForEachLeaf(PrintLeaf); + } + + template + void BPlusTree::ValidateTree(void) + { + int i, last=-9999; + DataStructures::Page *cur = GetListHead(); + while (cur) + { + RakAssert(cur->size>0); + for (i=0; i < cur->size; i++) + { + RakAssert(cur->data[i]==cur->keys[i]); + if (last!=-9999) + { + RakAssert(cur->data[i]>last); + } + last=cur->data[i]; + } + cur=cur->next; + } + if (root && root->isLeaf==false) + ValidateTreeRecursive(root); + } + template + void BPlusTree::ValidateTreeRecursive(Page *cur) + { + RakAssert(cur==root || cur->size>=order/2); + + if (cur->children[0]->isLeaf) + { + RakAssert(cur->children[0]->keys[0] < cur->keys[0]); + for (int i=0; i < cur->size; i++) + { + RakAssert(cur->children[i+1]->keys[0]==cur->keys[i]); + } + } + else + { + for (int i=0; i < cur->size+1; i++) + ValidateTreeRecursive(cur->children[i]); + } + } + + template + void BPlusTree::PrintGraph(void) + { + DataStructures::Queue *> queue; + queue.Push(root); + queue.Push(0); + DataStructures::Page *ptr; + int i,j; + if (root) + { + RAKNET_DEBUG_PRINTF("%p(", root); + for (i=0; i < root->size; i++) + { + RAKNET_DEBUG_PRINTF("%i ", root->keys[i]); + } + RAKNET_DEBUG_PRINTF(") "); + RAKNET_DEBUG_PRINTF("\n"); + } + while (queue.Size()) + { + ptr=queue.Pop(); + if (ptr==0) + RAKNET_DEBUG_PRINTF("\n"); + else if (ptr->isLeaf==false) + { + for (i=0; i < ptr->size+1; i++) + { + RAKNET_DEBUG_PRINTF("%p(", ptr->children[i]); + //RAKNET_DEBUG_PRINTF("(", ptr->children[i]); + for (j=0; j < ptr->children[i]->size; j++) + RAKNET_DEBUG_PRINTF("%i ", ptr->children[i]->keys[j]); + RAKNET_DEBUG_PRINTF(") "); + queue.Push(ptr->children[i]); + } + queue.Push(0); + RAKNET_DEBUG_PRINTF(" -- "); + } + } + RAKNET_DEBUG_PRINTF("\n"); + } +} +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + +// Code to test this hellish data structure. +/* +#include "DS_BPlusTree.h" +#include + +// Handle underflow on root. If there is only one item left then I can go downwards. +// Make sure I keep the leftmost pointer valid by traversing it +// When I free a leaf, be sure to adjust the pointers around it. + +#include "Rand.h" + +void main(void) +{ + DataStructures::BPlusTree btree; + DataStructures::List haveList, removedList; + int temp; + int i, j, index; + int testSize; + bool b; + + for (testSize=0; testSize < 514; testSize++) + { + RAKNET_DEBUG_PRINTF("TestSize=%i\n", testSize); + + for (i=0; i < testSize; i++) + haveList.Insert(i); + + for (i=0; i < testSize; i++) + { + index=i+randomMT()%(testSize-i); + temp=haveList[index]; + haveList[index]=haveList[i]; + haveList[i]=temp; + } + + for (i=0; i + * + * OR + * + * AVLBalancedBinarySearchTree + * + * Use the AVL balanced tree if you want the tree to be balanced after every deletion and addition. This avoids the potential + * worst case scenario where ordered input to a binary search tree gives linear search time results. It's not needed + * if input will be evenly distributed, in which case the search time is O (log n). The search time for the AVL + * balanced binary tree is O (log n) irregardless of input. + * + * Has the following member functions + * unsigned int Height() - Returns the height of the tree at the optional specified starting index. Default is the root + * add(element) - adds an element to the BinarySearchTree + * bool del(element) - deletes the node containing element if the element is in the tree as defined by a comparison with the == operator. Returns true on success, false if the element is not found + * bool IsInelement) - returns true if element is in the tree as defined by a comparison with the == operator. Otherwise returns false + * DisplayInorder(array) - Fills an array with an inorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPreorder(array) - Fills an array with an preorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayPostorder(array) - Fills an array with an postorder search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * DisplayBreadthFirstSearch(array) - Fills an array with a breadth first search of the elements in the tree. USER IS REPONSIBLE FOR ALLOCATING THE ARRAY!. + * clear - Destroys the tree. Same as calling the destructor + * unsigned int Height() - Returns the height of the tree + * unsigned int size() - returns the size of the BinarySearchTree + * GetPointerToNode(element) - returns a pointer to the comparision element in the tree, allowing for direct modification when necessary with complex data types. + * Be warned, it is possible to corrupt the tree if the element used for comparisons is modified. Returns NULL if the item is not found + * + * + * EXAMPLE + * @code + * BinarySearchTree A; + * A.Add(10); + * A.Add(15); + * A.Add(5); + * int* array = RakNet::OP_NEW(A.Size()); + * A.DisplayInorder(array); + * array[0]; // returns 5 + * array[1]; // returns 10 + * array[2]; // returns 15 + * @endcode + * compress - reallocates memory to fit the number of elements. Best used when the number of elements decreases + * + * clear - empties the BinarySearchTree and returns storage + * The assignment and copy constructors are defined + * + * \note The template type must have the copy constructor and + * assignment operator defined and must work with >, <, and == All + * elements in the tree MUST be distinct The assignment operator is + * defined between BinarySearchTree and AVLBalancedBinarySearchTree + * as long as they are of the same template type. However, passing a + * BinarySearchTree to an AVLBalancedBinarySearchTree will lose its + * structure unless it happened to be AVL balanced to begin with + * Requires queue_linked_list.cpp for the breadth first search used + * in the copy constructor, overloaded assignment operator, and + * display_breadth_first_search. + * + * + */ + template + class RAK_DLL_EXPORT BinarySearchTree + { + + public: + + struct node + { + BinarySearchTreeType* item; + node* left; + node* right; + }; + + BinarySearchTree(); + virtual ~BinarySearchTree(); + BinarySearchTree( const BinarySearchTree& original_type ); + BinarySearchTree& operator= ( const BinarySearchTree& original_copy ); + unsigned int Size( void ); + void Clear( void ); + unsigned int Height( node* starting_node = 0 ); + node* Add ( const BinarySearchTreeType& input ); + node* Del( const BinarySearchTreeType& input ); + bool IsIn( const BinarySearchTreeType& input ); + void DisplayInorder( BinarySearchTreeType* return_array ); + void DisplayPreorder( BinarySearchTreeType* return_array ); + void DisplayPostorder( BinarySearchTreeType* return_array ); + void DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ); + BinarySearchTreeType*& GetPointerToNode( const BinarySearchTreeType& element ); + + protected: + + node* root; + + enum Direction_Types + { + NOT_FOUND, LEFT, RIGHT, ROOT + } direction; + unsigned int HeightRecursive( node* current ); + unsigned int BinarySearchTree_size; + node*& Find( const BinarySearchTreeType& element, node** parent ); + node*& FindParent( const BinarySearchTreeType& element ); + void DisplayPostorderRecursive( node* current, BinarySearchTreeType* return_array, unsigned int& index ); + void FixTree( node* current ); + + }; + + /// An AVLBalancedBinarySearchTree is a binary tree that is always balanced + template + class RAK_DLL_EXPORT AVLBalancedBinarySearchTree : public BinarySearchTree + { + + public: + AVLBalancedBinarySearchTree() {} + virtual ~AVLBalancedBinarySearchTree(); + void Add ( const BinarySearchTreeType& input ); + void Del( const BinarySearchTreeType& input ); + BinarySearchTree& operator= ( BinarySearchTree& original_copy ) + { + return BinarySearchTree::operator= ( original_copy ); + } + + private: + void BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ); + void RotateRight( typename BinarySearchTree::node *C ); + void RotateLeft( typename BinarySearchTree::node* C ); + void DoubleRotateRight( typename BinarySearchTree::node *A ); + void DoubleRotateLeft( typename BinarySearchTree::node* A ); + bool RightHigher( typename BinarySearchTree::node* A ); + bool LeftHigher( typename BinarySearchTree::node* A ); + }; + + template + void AVLBalancedBinarySearchTree::BalanceTree( typename BinarySearchTree::node* current, bool rotateOnce ) + { + int left_height, right_height; + + while ( current ) + { + if ( current->left == 0 ) + left_height = 0; + else + left_height = Height( current->left ); + + if ( current->right == 0 ) + right_height = 0; + else + right_height = Height( current->right ); + + if ( right_height - left_height == 2 ) + { + if ( RightHigher( current->right ) ) + RotateLeft( current->right ); + else + DoubleRotateLeft( current ); + + if ( rotateOnce ) + break; + } + + else + if ( right_height - left_height == -2 ) + { + if ( LeftHigher( current->left ) ) + RotateRight( current->left ); + else + DoubleRotateRight( current ); + + if ( rotateOnce ) + break; + } + + if ( current == this->root ) + break; + + current = FindParent( *( current->item ) ); + + } + } + + template + void AVLBalancedBinarySearchTree::Add ( const BinarySearchTreeType& input ) + { + + typename BinarySearchTree::node * current = BinarySearchTree::Add ( input ); + BalanceTree( current, true ); + } + + template + void AVLBalancedBinarySearchTree::Del( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * current = BinarySearchTree::Del( input ); + BalanceTree( current, false ); + + } + + template + bool AVLBalancedBinarySearchTree::RightHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->right ) > Height( A->left ); + } + + template + bool AVLBalancedBinarySearchTree::LeftHigher( typename BinarySearchTree::node *A ) + { + if ( A == 0 ) + return false; + + return Height( A->left ) > Height( A->right ); + } + + template + void AVLBalancedBinarySearchTree::RotateRight( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->right; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->left = D; + + C->right = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateRight( typename BinarySearchTree::node *A ) + { + // The left side of the left child must be higher for the tree to balance with a right rotation. If it isn't, rotate it left before the normal rotation so it is. + RotateLeft( A->left->right ); + RotateRight( A->left ); + } + + template + void AVLBalancedBinarySearchTree::RotateLeft( typename BinarySearchTree::node *C ) + { + typename BinarySearchTree::node * A, *B, *D; + /* + RIGHT ROTATION + + A = parent(b) + b= parent(c) + c = node to rotate around + + A + | // Either direction + B + / \ + C + / \ + D + + TO + + A + | // Either Direction + C + / \ + B + / \ + D + + + + + */ + + B = FindParent( *( C->item ) ); + A = FindParent( *( B->item ) ); + D = C->left; + + if ( A ) + { + // Direction was set by the last find_parent call + + if ( this->direction == this->LEFT ) + A->left = C; + else + A->right = C; + } + + else + this->root = C; // If B has no parent parent then B must have been the root node + + B->right = D; + + C->left = B; + } + + template + void AVLBalancedBinarySearchTree::DoubleRotateLeft( typename BinarySearchTree::node *A ) + { + // The left side of the right child must be higher for the tree to balance with a left rotation. If it isn't, rotate it right before the normal rotation so it is. + RotateRight( A->right->left ); + RotateLeft( A->right ); + } + + template + AVLBalancedBinarySearchTree::~AVLBalancedBinarySearchTree() + { + this->Clear(); + } + + template + unsigned int BinarySearchTree::Size( void ) + { + return BinarySearchTree_size; + } + + template + unsigned int BinarySearchTree::Height( typename BinarySearchTree::node* starting_node ) + { + if ( BinarySearchTree_size == 0 || starting_node == 0 ) + return 0; + else + return HeightRecursive( starting_node ); + } + + // Recursively return the height of a binary tree + template + unsigned int BinarySearchTree::HeightRecursive( typename BinarySearchTree::node* current ) + { + unsigned int left_height = 0, right_height = 0; + + if ( ( current->left == 0 ) && ( current->right == 0 ) ) + return 1; // Leaf + + if ( current->left != 0 ) + left_height = 1 + HeightRecursive( current->left ); + + if ( current->right != 0 ) + right_height = 1 + HeightRecursive( current->right ); + + if ( left_height > right_height ) + return left_height; + else + return right_height; + } + + template + BinarySearchTree::BinarySearchTree() + { + BinarySearchTree_size = 0; + root = 0; + } + + template + BinarySearchTree::~BinarySearchTree() + { + this->Clear(); + } + + template + BinarySearchTreeType*& BinarySearchTree::GetPointerToNode( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * tempnode; + static BinarySearchTreeType* dummyptr = 0; + tempnode = Find ( element, &tempnode ); + + if ( this->direction == this->NOT_FOUND ) + return dummyptr; + + return tempnode->item; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::Find( const BinarySearchTreeType& element, typename BinarySearchTree::node** parent ) + { + static typename BinarySearchTree::node * current; + + current = this->root; + *parent = 0; + this->direction = this->ROOT; + + if ( BinarySearchTree_size == 0 ) + { + this->direction = this->NOT_FOUND; + return current = 0; + } + + // Check if the item is at the root + if ( element == *( current->item ) ) + { + this->direction = this->ROOT; + return current; + } + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) + { + // Move pointer + + if ( element < *( current->item ) ) + { + *parent = current; + this->direction = this->LEFT; + current = current->left; + } + + else + if ( element > *( current->item ) ) + { + *parent = current; + this->direction = this->RIGHT; + current = current->right; + } + + if ( current == 0 ) + break; + + // Check if new position holds the item + if ( element == *( current->item ) ) + { + return current; + } + } + + + this->direction = this->NOT_FOUND; + return current = 0; + } + + template + typename BinarySearchTree::node*& BinarySearchTree::FindParent( const BinarySearchTreeType& element ) + { + static typename BinarySearchTree::node * parent; + Find ( element, &parent ); + return parent; + } + + // Performs a series of value swaps starting with current to fix the tree if needed + template + void BinarySearchTree::FixTree( typename BinarySearchTree::node* current ) + { + BinarySearchTreeType temp; + + while ( 1 ) + { + if ( ( ( current->left ) != 0 ) && ( *( current->item ) < *( current->left->item ) ) ) + { + // Swap the current value with the one to the left + temp = *( current->left->item ); + *( current->left->item ) = *( current->item ); + *( current->item ) = temp; + current = current->left; + } + + else + if ( ( ( current->right ) != 0 ) && ( *( current->item ) > *( current->right->item ) ) ) + { + // Swap the current value with the one to the right + temp = *( current->right->item ); + *( current->right->item ) = *( current->item ); + *( current->item ) = temp; + current = current->right; + } + + else + break; // current points to the right place so quit + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Del( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * node_to_delete, *current, *parent; + + if ( BinarySearchTree_size == 0 ) + return 0; + + if ( BinarySearchTree_size == 1 ) + { + Clear(); + return 0; + } + + node_to_delete = Find( input, &parent ); + + if ( direction == NOT_FOUND ) + return 0; // Couldn't find the element + + current = node_to_delete; + + // Replace the deleted node with the appropriate value + if ( ( current->right ) == 0 && ( current->left ) == 0 ) // Leaf node, just remove it + { + + if ( parent ) + { + if ( direction == LEFT ) + parent->left = 0; + else + parent->right = 0; + } + + RakNet::OP_DELETE(node_to_delete->item); + RakNet::OP_DELETE(node_to_delete); + BinarySearchTree_size--; + return parent; + } + else + if ( ( current->right ) != 0 && ( current->left ) == 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + } + + else + root = current->right; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item); + + RakNet::OP_DELETE(node_to_delete); + + BinarySearchTree_size--; + + return parent; + } + else + if ( ( current->right ) == 0 && ( current->left ) != 0 ) // Node has only one child, delete it and cause the parent to point to that child + { + + if ( parent ) + { + if ( direction == RIGHT ) + parent->right = current->left; + else + parent->left = current->left; + } + + else + root = current->left; // Without a parent this must be the root node + + RakNet::OP_DELETE(node_to_delete->item); + + RakNet::OP_DELETE(node_to_delete); + + BinarySearchTree_size--; + + return parent; + } + else // Go right, then as left as far as you can + { + parent = current; + direction = RIGHT; + current = current->right; // Must have a right branch because the if statements above indicated that it has 2 branches + + while ( current->left ) + { + direction = LEFT; + parent = current; + current = current->left; + } + + // Replace the value held by the node to RakNet::OP_DELETE(with the value pointed to by current); + *( node_to_delete->item ) = *( current->item ); + + // Delete current. + // If it is a leaf node just delete it + if ( current->right == 0 ) + { + if ( direction == RIGHT ) + parent->right = 0; + else + parent->left = 0; + + RakNet::OP_DELETE(current->item); + + RakNet::OP_DELETE(current); + + BinarySearchTree_size--; + + return parent; + } + + else + { + // Skip this node and make its parent point to its right branch + + if ( direction == RIGHT ) + parent->right = current->right; + else + parent->left = current->right; + + RakNet::OP_DELETE(current->item); + + RakNet::OP_DELETE(current); + + BinarySearchTree_size--; + + return parent; + } + } + } + + template + typename BinarySearchTree::node* BinarySearchTree::Add ( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * current, *parent; + + // Add the new element to the tree according to the following alogrithm: + // 1. If the current node is empty add the new leaf + // 2. If the element is less than the current node then go down the left branch + // 3. If the element is greater than the current node then go down the right branch + + if ( BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 1; + root = RakNet::OP_NEW(); + root->item = RakNet::OP_NEW(); + *( root->item ) = input; + root->left = 0; + root->right = 0; + + return root; + } + + else + { + // start at the root + current = parent = root; + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while ( true ) // This loop traverses the tree to find a spot for insertion + { + + if ( input < *( current->item ) ) + { + if ( current->left == 0 ) + { + current->left = RakNet::OP_NEW(); + current->left->item = RakNet::OP_NEW(); + current = current->left; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + parent = current; + current = current->left; + } + } + + else + if ( input > *( current->item ) ) + { + if ( current->right == 0 ) + { + current->right = RakNet::OP_NEW(); + current->right->item = RakNet::OP_NEW(); + current = current->right; + current->left = 0; + current->right = 0; + *( current->item ) = input; + + BinarySearchTree_size++; + return current; + } + + else + { + parent = current; + current = current->right; + } + } + + else + return 0; // ((input == current->item) == true) which is not allowed since the tree only takes discrete values. Do nothing + } + } + } + + template + bool BinarySearchTree::IsIn( const BinarySearchTreeType& input ) + { + typename BinarySearchTree::node * parent; + find( input, &parent ); + + if ( direction != NOT_FOUND ) + return true; + else + return false; + } + + + template + void BinarySearchTree::DisplayInorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + bool just_printed = false; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + // Go left if the following 2 conditions are true + // I can go left + // I did not just move up from a right child + // I did not just move up from a left child + + current = current->left; + direction = ROOT; // Reset the direction + } + + else + if ( ( direction != RIGHT ) && ( just_printed == false ) ) + { + // Otherwise, print the current node if the following 3 conditions are true: + // I did not just move up from a right child + // I did not print this ndoe last cycle + + return_array[ index++ ] = *( current->item ); + just_printed = true; + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + // Otherwise, go right if the following 2 conditions are true + // I did not just move up from a right child + // I can go right + + current = current->right; + direction = ROOT; // Reset the direction + just_printed = false; + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + just_printed = false; + } + } + } + + template + void BinarySearchTree::DisplayPreorder( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current, *parent; + + unsigned int index = 0; + + current = root; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + + direction = ROOT; // Reset the direction + return_array[ index++ ] = *( current->item ); + + while ( index != BinarySearchTree_size ) + { + // direction is set by the find function and holds the direction of the parent to the last node visited. It is used to prevent revisiting nodes + + if ( ( current->left != 0 ) && ( direction != LEFT ) && ( direction != RIGHT ) ) + { + + current = current->left; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + if ( ( current->right != 0 ) && ( direction != RIGHT ) ) + { + current = current->right; + direction = ROOT; + + // Everytime you move a node print it + return_array[ index++ ] = *( current->item ); + } + + else + { + // Otherwise I've done everything I can. Move up the tree one node + parent = FindParent( *( current->item ) ); + current = parent; + } + } + } + + template + inline void BinarySearchTree::DisplayPostorder( BinarySearchTreeType* return_array ) + { + unsigned int index = 0; + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + DisplayPostorderRecursive( root, return_array, index ); + } + + + // Recursively do a postorder traversal + template + void BinarySearchTree::DisplayPostorderRecursive( typename BinarySearchTree::node* current, BinarySearchTreeType* return_array, unsigned int& index ) + { + if ( current->left != 0 ) + DisplayPostorderRecursive( current->left, return_array, index ); + + if ( current->right != 0 ) + DisplayPostorderRecursive( current->right, return_array, index ); + + return_array[ index++ ] = *( current->item ); + + } + + + template + void BinarySearchTree::DisplayBreadthFirstSearch( BinarySearchTreeType* return_array ) + { + typename BinarySearchTree::node * current; + unsigned int index = 0; + + // Display the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + if ( BinarySearchTree_size == 0 ) + return ; // Do nothing for an empty tree + + else + if ( BinarySearchTree_size == 1 ) + { + return_array[ 0 ] = *( root->item ); + return ; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( root ); + + do + { + current = tree_queue.Pop(); + return_array[ index++ ] = *( current->item ); + + // Add the child or children of the tree I am copying from to the queue + + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + + template + BinarySearchTree::BinarySearchTree( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + root = 0; + + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ) ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + } + + template + BinarySearchTree& BinarySearchTree::operator= ( const BinarySearchTree& original_copy ) + { + typename BinarySearchTree::node * current; + + if ( ( &original_copy ) == this ) + return *this; + + Clear(); // Remove the current tree + + // This is a copy of the constructor. A bug in Visual C++ made it so if I just put the constructor call here the variable assignments were ignored. + BinarySearchTree_size = 0; + + root = 0; + + + // Copy the tree using a breadth first search + // Put the children of the current node into the queue + // Pop the queue, put its children into the queue, repeat until queue is empty + if ( original_copy.BinarySearchTree_size == 0 ) + { + BinarySearchTree_size = 0; + } + + else + { + DataStructures::QueueLinkedList tree_queue; + + // Add the root of the tree I am copying from + tree_queue.Push( original_copy.root ); + + do + { + current = tree_queue.Pop(); + + Add ( *( current->item ) ) + + ; + + // Add the child or children of the tree I am copying from to the queue + if ( current->left != 0 ) + tree_queue.Push( current->left ); + + if ( current->right != 0 ) + tree_queue.Push( current->right ); + + } + + while ( tree_queue.Size() > 0 ); + } + + return *this; + } + + template + inline void BinarySearchTree::Clear ( void ) + { + typename BinarySearchTree::node * current, *parent; + + current = root; + + while ( BinarySearchTree_size > 0 ) + { + if ( BinarySearchTree_size == 1 ) + { + RakNet::OP_DELETE(root->item); + RakNet::OP_DELETE(root); + root = 0; + BinarySearchTree_size = 0; + } + + else + { + if ( current->left != 0 ) + { + current = current->left; + } + + else + if ( current->right != 0 ) + { + current = current->right; + } + + else // leaf + { + // Not root node so must have a parent + parent = FindParent( *( current->item ) ); + + if ( ( parent->left ) == current ) + parent->left = 0; + else + parent->right = 0; + + RakNet::OP_DELETE(current->item); + + RakNet::OP_DELETE(current); + + current = parent; + + BinarySearchTree_size--; + } + } + } + } + +} // End namespace + +#endif + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif diff --git a/Multiplayer/raknet/DS_BytePool.h b/Multiplayer/raknet/DS_BytePool.h new file mode 100644 index 000000000..0dae785ad --- /dev/null +++ b/Multiplayer/raknet/DS_BytePool.h @@ -0,0 +1,40 @@ +#ifndef __BYTE_POOL_H +#define __BYTE_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_MemoryPool.h" +#include "Export.h" +#include "SimpleMutex.h" +#include "RakAssert.h" + +// #define _DISABLE_BYTE_POOL +// #define _THREADSAFE_BYTE_POOL + +namespace DataStructures +{ + // Allocate some number of bytes from pools. Uses the heap if necessary. + class RAK_DLL_EXPORT BytePool + { + public: + BytePool(); + ~BytePool(); + // Should be at least 8 times bigger than 8192 + void SetPageSize(int size); + unsigned char* Allocate(int bytesWanted); + void Release(unsigned char *data); + void Clear(void); + protected: + MemoryPool pool128; + MemoryPool pool512; + MemoryPool pool2048; + MemoryPool pool8192; +#ifdef _THREADSAFE_BYTE_POOL + SimpleMutex mutex128; + SimpleMutex mutex512; + SimpleMutex mutex2048; + SimpleMutex mutex8192; +#endif + }; +} + +#endif diff --git a/Multiplayer/raknet/DS_ByteQueue.h b/Multiplayer/raknet/DS_ByteQueue.h new file mode 100644 index 000000000..9a7f1f7c3 --- /dev/null +++ b/Multiplayer/raknet/DS_ByteQueue.h @@ -0,0 +1,46 @@ +/// \file +/// \brief \b [Internal] Byte queue +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __BYTE_QUEUE_H +#define __BYTE_QUEUE_H + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + class ByteQueue + { + public: + 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 diff --git a/Multiplayer/raknet/DS_Heap.h b/Multiplayer/raknet/DS_Heap.h new file mode 100644 index 000000000..db8cb157e --- /dev/null +++ b/Multiplayer/raknet/DS_Heap.h @@ -0,0 +1,251 @@ +/// \file +/// \brief \b [Internal] Heap (Also serves as a priority queue) +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAKNET_HEAP_H +#define __RAKNET_HEAP_H + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "Export.h" +#include "RakAssert.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 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 heap; + }; + + template + Heap::Heap() + { + } + + template + Heap::~Heap() + { + Clear(); + } + + template + void Heap::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 + data_type Heap::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[startingIndex].data; + + // Move the last element to the head, and re-heapify + heap[startingIndex]=heap[heap.Size()-1]; + + unsigned currentIndex,leftChild,rightChild; + weight_type currentWeight; + currentIndex=startingIndex; + currentWeight=heap[startingIndex].weight; + heap.RemoveFromEnd(); + +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + leftChild=LeftChild(currentIndex); + rightChild=RightChild(currentIndex); + if (leftChild >= heap.Size()) + { + // Done + return returnValue; + } + if (rightChild >= heap.Size()) + { + // Only left node. + if ((isMaxHeap==true && currentWeight < heap[leftChild].weight) || + (isMaxHeap==false && currentWeight > heap[leftChild].weight)) + Swap(leftChild, currentIndex); + + return returnValue; + } + else + { + // Swap with the bigger/smaller of the two children and continue + if (isMaxHeap) + { + if (heap[leftChild].weight <= currentWeight && heap[rightChild].weight <= currentWeight) + return returnValue; + + if (heap[leftChild].weight > heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + else + { + if (heap[leftChild].weight >= currentWeight && heap[rightChild].weight >= currentWeight) + return returnValue; + + if (heap[leftChild].weight < heap[rightChild].weight) + { + Swap(leftChild, currentIndex); + currentIndex=leftChild; + } + else + { + Swap(rightChild, currentIndex); + currentIndex=rightChild; + } + } + } + } + } + + template + data_type Heap::Peek(const unsigned startingIndex) const + { + return heap[startingIndex].data; + } + + template + weight_type Heap::PeekWeight(const unsigned startingIndex) const + { + return heap[startingIndex].weight; + } + + template + void Heap::Clear(void) + { + heap.Clear(); + } + + template + data_type& Heap::operator[] ( const unsigned int position ) const + { + return heap[position].data; + } + template + unsigned Heap::Size(void) const + { + return heap.Size(); + } + + template + unsigned Heap::LeftChild(const unsigned i) const + { + return i*2+1; + } + + template + unsigned Heap::RightChild(const unsigned i) const + { + return i*2+2; + } + + template + unsigned Heap::Parent(const unsigned i) const + { +#ifdef _DEBUG + RakAssert(i!=0); +#endif + return (i-1)/2; + } + + template + void Heap::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 diff --git a/Multiplayer/raknet/DS_HuffmanEncodingTree.h b/Multiplayer/raknet/DS_HuffmanEncodingTree.h new file mode 100644 index 000000000..fa2cd256e --- /dev/null +++ b/Multiplayer/raknet/DS_HuffmanEncodingTree.h @@ -0,0 +1,71 @@ +/// \file +/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __HUFFMAN_ENCODING_TREE +#define __HUFFMAN_ENCODING_TREE + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTreeNode.h" +#include "BitStream.h" +#include "Export.h" +#include "DS_LinkedList.h" + +/// This generates special cases of the huffman encoding tree using 8 bit keys with the additional condition that unused combinations of 8 bits are treated as a frequency of 1 +class RAK_DLL_EXPORT HuffmanEncodingTree +{ + +public: + HuffmanEncodingTree(); + ~HuffmanEncodingTree(); + + /// Pass an array of bytes to array and a preallocated BitStream to receive the output + /// \param [in] input Array of bytes to encode + /// \param [in] sizeInBytes size of \a input + /// \param [out] output The bitstream to write to + void EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output ); + + // Decodes an array encoded by EncodeArray() + unsigned DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output ); + void DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output ); + + /// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree + void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] ); + + /// Free the memory used by the tree + void FreeMemory( void ); + +private: + + /// The root node of the tree + + HuffmanEncodingTreeNode *root; + + /// Used to hold bit encoding for one character + + + struct CharacterEncoding + { + unsigned char* encoding; + unsigned short bitLength; + }; + + CharacterEncoding encodingTable[ 256 ]; + + void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList *huffmanEncodingTreeNodeList ) const; +}; + +#endif diff --git a/Multiplayer/raknet/DS_HuffmanEncodingTreeFactory.h b/Multiplayer/raknet/DS_HuffmanEncodingTreeFactory.h new file mode 100644 index 000000000..0b4f7fb71 --- /dev/null +++ b/Multiplayer/raknet/DS_HuffmanEncodingTreeFactory.h @@ -0,0 +1,61 @@ +/// \file +/// \brief \b [Internal] Creates instances of the class HuffmanEncodingTree +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __HUFFMAN_ENCODING_TREE_FACTORY +#define __HUFFMAN_ENCODING_TREE_FACTORY + +#include "RakMemoryOverride.h" +class HuffmanEncodingTree; + +/// \brief Creates instances of the class HuffmanEncodingTree +/// +/// This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree +class HuffmanEncodingTreeFactory +{ +public: + /// 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 diff --git a/Multiplayer/raknet/DS_HuffmanEncodingTreeNode.h b/Multiplayer/raknet/DS_HuffmanEncodingTreeNode.h new file mode 100644 index 000000000..b4982c32e --- /dev/null +++ b/Multiplayer/raknet/DS_HuffmanEncodingTreeNode.h @@ -0,0 +1,30 @@ +/// \file +/// \brief \b [Internal] A single node in the Huffman Encoding Tree. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __HUFFMAN_ENCODING_TREE_NODE +#define __HUFFMAN_ENCODING_TREE_NODE + +struct HuffmanEncodingTreeNode +{ + unsigned char value; + unsigned weight; + HuffmanEncodingTreeNode *left; + HuffmanEncodingTreeNode *right; + HuffmanEncodingTreeNode *parent; +}; + +#endif diff --git a/Multiplayer/raknet/DS_LinkedList.h b/Multiplayer/raknet/DS_LinkedList.h new file mode 100644 index 000000000..2790b1a07 --- /dev/null +++ b/Multiplayer/raknet/DS_LinkedList.h @@ -0,0 +1,1259 @@ +/// \file +/// \brief \b [Internal] Straightforward linked list data structure. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __LINKED_LIST_H +#define __LINKED_LIST_H + +#include "Export.h" +#include "RakMemoryOverride.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 +{ + // Prototype to prevent error in CircularLinkedList class when a reference is made to a LinkedList class + template + class RAK_DLL_EXPORT LinkedList; + + /** + * \brief (Circular) Linked List ADT (Doubly Linked Pointer to Node Style) - + * + * By Kevin Jenkins (http://www.rakkar.org) + * Initilize with the following command + * LinkedList + * OR + * CircularLinkedList + * + * Has the following member functions + * - size: returns number of elements in the linked list + * - insert(item): inserts @em item at the current position in + * the LinkedList. + * - add(item): inserts @em item after the current position in + * the LinkedList. Does not increment the position + * - replace(item): replaces the element at the current position @em item. + * - peek: returns the element at the current position + * - pop: returns the element at the current position and deletes it + * - del: deletes the current element. Does nothing for an empty list. + * - clear: empties the LinkedList and returns storage + * - bool IsInitem): Does a linear search for @em item. Does not set + * the position to it, only returns true on item found, false otherwise + * - bool find(item): Does a linear search for @em item and sets the current + * position to point to it if and only if the item is found. Returns true + * on item found, false otherwise + * - sort: Sorts the elements of the list with a mergesort and sets the + * current pointer to the first element + * - concatenate(list L): This appends L to the current list + * - ++(prefix): moves the pointer one element up in the list and returns the + * appropriate copy of the element in the list + * - --(prefix): moves the pointer one element back in the list and returns + * the appropriate copy of the element in the list + * - beginning - moves the pointer to the start of the list. For circular + * linked lists this is first 'position' created. You should call this + * after the sort function to read the first value. + * - end - moves the pointer to the end of the list. For circular linked + * lists this is one less than the first 'position' created + * The assignment and copy constructor operators are defined + * + * \note + * 1. LinkedList and CircularLinkedList are exactly the same except LinkedList + * won't let you wrap around the root and lets you jump to two positions + * relative to the root/ + * 2. Postfix ++ and -- can be used but simply call the prefix versions. + * + * + * EXAMPLE: + * @code + * LinkedList A; // Creates a Linked List of integers called A + * CircularLinkedList B; // Creates a Circular Linked List of + * // integers called B + * + * A.Insert(20); // Adds 20 to A. A: 20 - current is 20 + * A.Insert(5); // Adds 5 to A. A: 5 20 - current is 5 + * A.Insert(1); // Adds 1 to A. A: 1 5 20 - current is 1 + * + * A.IsIn1); // returns true + * A.IsIn200); // returns false + * A.Find(5); // returns true and sets current to 5 + * A.Peek(); // returns 5 + * A.Find(1); // returns true and sets current to 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 5 + * + * A.Replace(10); // Replaces 5 with 10. + * A.Peek(); // Returns 10 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 10 + * + * A.Del(); // Deletes 10. Current points to the next element, which is 20 + * A.Peek(); // Returns 20 + * + * A.Beginning(); // Current points to the beginning of the list at 1 + * + * (++A).Peek(); // Returns 5 + * A.Peek(); // Returns 20 + * + * A.Clear(); // Deletes all nodes in A + * + * A.Insert(5); // A: 5 - current is 5 + * A.Insert(6); // A: 6 5 - current is 6 + * A.Insert(7); // A: 7 6 5 - current is 7 + * + * A.Clear(); + * B.Clear(); + * + * B.Add(10); + * B.Add(20); + * B.Add(30); + * B.Add(5); + * B.Add(2); + * B.Add(25); + * // Sorts the numbers in the list and sets the current pointer to the + * // first element + * B.sort(); + * + * // Postfix ++ just calls the prefix version and has no functional + * // difference. + * B.Peek(); // Returns 2 + * B++; + * B.Peek(); // Returns 5 + * B++; + * B.Peek(); // Returns 10 + * B++; + * B.Peek(); // Returns 20 + * B++; + * B.Peek(); // Returns 25 + * B++; + * B.Peek(); // Returns 30 + * @endcode + */ + template + + class CircularLinkedList + { + + public: + + struct node + { + CircularLinkedListType item; + + node* previous; + node* next; + }; + + CircularLinkedList(); + ~CircularLinkedList(); + CircularLinkedList( const CircularLinkedList& original_copy ); + // CircularLinkedList(LinkedList original_copy) {CircularLinkedList(original_copy);} // Converts linked list to circular type + bool operator= ( const CircularLinkedList& original_copy ); + CircularLinkedList& operator++(); // CircularLinkedList A; ++A; + CircularLinkedList& operator++( int ); // Circular_Linked List A; A++; + CircularLinkedList& operator--(); // CircularLinkedList A; --A; + CircularLinkedList& operator--( int ); // Circular_Linked List A; A--; + bool IsIn( const CircularLinkedListType& input ); + bool Find( const CircularLinkedListType& input ); + void Insert( const CircularLinkedListType& input ); + + CircularLinkedListType& Add ( const CircularLinkedListType& input ) + + ; // Adds after the current position + void Replace( const CircularLinkedListType& input ); + + void Del( void ); + + unsigned int Size( void ); + + CircularLinkedListType& Peek( void ); + + CircularLinkedListType Pop( void ); + + void Clear( void ); + + void Sort( void ); + + void Beginning( void ); + + void End( void ); + + void Concatenate( const CircularLinkedList& L ); + + protected: + unsigned int list_size; + + node *root; + + node *position; + + node* FindPointer( const CircularLinkedListType& input ); + + private: + CircularLinkedList Merge( CircularLinkedList L1, CircularLinkedList L2 ); + + CircularLinkedList Mergesort( const CircularLinkedList& L ); + }; + + template + + class LinkedList : public CircularLinkedList + { + + public: + LinkedList() + {} + + LinkedList( const LinkedList& original_copy ); + ~LinkedList(); + bool operator= ( const LinkedList& original_copy ); + LinkedList& operator++(); // LinkedList A; ++A; + LinkedList& operator++( int ); // Linked List A; A++; + LinkedList& operator--(); // LinkedList A; --A; + LinkedList& operator--( int ); // Linked List A; A--; + + private: + LinkedList Merge( LinkedList L1, LinkedList L2 ); + LinkedList Mergesort( const LinkedList& L ); + + }; + + + template + inline void CircularLinkedList::Beginning( void ) + { + if ( this->root ) + this->position = this->root; + } + + template + inline void CircularLinkedList::End( void ) + { + if ( this->root ) + this->position = this->root->previous; + } + + template + bool LinkedList::operator= ( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + + template + CircularLinkedList::CircularLinkedList() + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + template + CircularLinkedList::~CircularLinkedList() + { + this->Clear(); + } + + template + LinkedList::~LinkedList() + { + this->Clear(); + } + + template + LinkedList::LinkedList( const LinkedList& original_copy ) + { + typename LinkedList::node * original_copy_pointer, *last, *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + CircularLinkedList::CircularLinkedList( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + return ; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item) = *((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + + + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + +#ifdef _MSC_VER +#pragma warning( disable : 4701 ) // warning C4701: local variable may be used without having been initialized +#endif + template + bool CircularLinkedList::operator= ( const CircularLinkedList& original_copy ) + { + node * original_copy_pointer; + node *last; + node *save_position; + + if ( ( &original_copy ) != this ) + { + + this->Clear(); + + + if ( original_copy.list_size == 0 ) + { + this->root = 0; + this->position = 0; + this->list_size = 0; + } + + else + if ( original_copy.list_size == 1 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + } + + else + { + // Setup the first part of the root node + original_copy_pointer = original_copy.root; + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + this->position = this->root; + // *(root->item)=*((original_copy.root)->item); + this->root->item = original_copy.root->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + do + { + // Save the current element + last = this->position; + + // Point to the next node in the source list + original_copy_pointer = original_copy_pointer->next; + + // Create a new node and point position to it + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + + // Copy the item to the new node + // *(position->item)=*(original_copy_pointer->item); + this->position->item = original_copy_pointer->item; + + if ( original_copy_pointer == original_copy.position ) + save_position = this->position; + + // Set the previous pointer for the new node + ( this->position->previous ) = last; + + // Set the next pointer for the old node to the new node + ( last->next ) = this->position; + + } + + while ( ( original_copy_pointer->next ) != ( original_copy.root ) ); + + // Complete the circle. Set the next pointer of the newest node to the root and the previous pointer of the root to the newest node + this->position->next = this->root; + + this->root->previous = this->position; + + this->list_size = original_copy.list_size; + + this->position = save_position; + } + } + + return true; + } + + template + void CircularLinkedList::Insert( const CircularLinkedListType& input ) + { + node * new_node; + + if ( list_size == 0 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + //*(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->root = this->position; // Since we're inserting into a 1 element list the old root is now the second item + this->list_size = 2; + } + + else + { + /* + + B + | + A --- C + + position->previous=A + new_node=B + position=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW(); + // new_node->item = RakNet::OP_NEW(); + + // *(new_node->item)=input; + new_node->item = input; + + // Point next of A to B + ( this->position->previous ) ->next = new_node; + + // Point last of B to A + new_node->previous = this->position->previous; + + // Point last of C to B + this->position->previous = new_node; + + // Point next of B to C + new_node->next = this->position; + + // Since the root pointer is bound to a node rather than an index this moves it back if you insert an element at the root + + if ( this->position == this->root ) + { + this->root = new_node; + this->position = this->root; + } + + // Increase the recorded size of the list by one + this->list_size++; + } + } + + template + CircularLinkedListType& CircularLinkedList::Add ( const CircularLinkedListType& input ) + { + node * new_node; + + if ( this->list_size == 0 ) + { + this->root = RakNet::OP_NEW(); + // root->item = RakNet::OP_NEW(); + // *(root->item)=input; + this->root->item = input; + this->root->next = this->root; + this->root->previous = this->root; + this->list_size = 1; + this->position = this->root; + // return *(position->item); + return this->position->item; + } + + else + if ( list_size == 1 ) + { + this->position = RakNet::OP_NEW(); + // position->item = RakNet::OP_NEW(); + this->root->next = this->position; + this->root->previous = this->position; + this->position->previous = this->root; + this->position->next = this->root; + // *(position->item)=input; + this->position->item = input; + this->list_size = 2; + this->position = this->root; // Don't move the position from the root + // return *(position->item); + return this->position->item; + } + + else + { + /* + + B + | + A --- C + + new_node=B + position=A + position->next=C + + Note that the order of the following statements is important */ + + new_node = RakNet::OP_NEW(); + // new_node->item = RakNet::OP_NEW(); + + // *(new_node->item)=input; + new_node->item = input; + + // Point last of B to A + new_node->previous = this->position; + + // Point next of B to C + new_node->next = ( this->position->next ); + + // Point last of C to B + ( this->position->next ) ->previous = new_node; + + // Point next of A to B + ( this->position->next ) = new_node; + + // Increase the recorded size of the list by one + this->list_size++; + + // return *(new_node->item); + return new_node->item; + } + } + + template + inline void CircularLinkedList::Replace( const CircularLinkedListType& input ) + { + if ( this->list_size > 0 ) + // *(position->item)=input; + this->position->item = input; + } + + template + void CircularLinkedList::Del() + { + node * new_position; + + if ( this->list_size == 0 ) + return ; + + else + if ( this->list_size == 1 ) + { + // RakNet::OP_DELETE(root->item); + RakNet::OP_DELETE(this->root); + this->root = this->position = 0; + this->list_size = 0; + } + + else + { + ( this->position->previous ) ->next = this->position->next; + ( this->position->next ) ->previous = this->position->previous; + new_position = this->position->next; + + if ( this->position == this->root ) + this->root = new_position; + + // RakNet::OP_DELETE(position->item); + RakNet::OP_DELETE(this->position); + + this->position = new_position; + + this->list_size--; + } + } + + template + bool CircularLinkedList::IsIn(const CircularLinkedListType& input ) + { + node * return_value, *old_position; + + old_position = this->position; + + return_value = FindPointer( input ); + this->position = old_position; + + if ( return_value != 0 ) + return true; + else + return false; // Can't find the item don't do anything + } + + template + bool CircularLinkedList::Find( const CircularLinkedListType& input ) + { + node * return_value; + + return_value = FindPointer( input ); + + if ( return_value != 0 ) + { + this->position = return_value; + return true; + } + + else + return false; // Can't find the item don't do anything + } + + template + typename CircularLinkedList::node* CircularLinkedList::FindPointer( const CircularLinkedListType& input ) + { + node * current; + + if ( this->list_size == 0 ) + return 0; + + current = this->root; + + // Search for the item starting from the root node and incrementing the pointer after every check + // If you wind up pointing at the root again you looped around the list so didn't find the item, in which case return 0 + do + { + // if (*(current->item) == input) return current; + + if ( current->item == input ) + return current; + + current = current->next; + } + + while ( current != this->root ); + + return 0; + + } + + template + inline unsigned int CircularLinkedList::Size( void ) + { + return this->list_size; + } + + template + inline CircularLinkedListType& CircularLinkedList::Peek( void ) + { + // return *(position->item); + return this->position->item; + } + + template + CircularLinkedListType CircularLinkedList::Pop( void ) + { + CircularLinkedListType element; + element = Peek(); + Del(); + return CircularLinkedListType( element ); // return temporary + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator++() + { + if ( this->list_size != 0 ) + position = position->next; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator++(int) + { + CircularLinkedList before; + before=*this; + operator++(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + CircularLinkedList& CircularLinkedList::operator--() + { + if ( this->list_size != 0 ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + CircularLinkedList& CircularLinkedList::operator--(int) + { + CircularLinkedList before; + before=*this; + operator--(); + return before; + } + */ + + template + CircularLinkedList& CircularLinkedList::operator--( int ) + { + return this->operator--(); + } + + template + void CircularLinkedList::Clear( void ) + { + if ( this->list_size == 0 ) + return ; + else + if ( this->list_size == 1 ) // {RakNet::OP_DELETE(root->item); RakNet::OP_DELETE(root);} + { + RakNet::OP_DELETE(this->root); + } + + else + { + node* current; + node* temp; + + current = this->root; + + do + { + temp = current; + current = current->next; + // RakNet::OP_DELETE(temp->item); + RakNet::OP_DELETE(temp); + } + + while ( current != this->root ); + } + + this->list_size = 0; + this->root = 0; + this->position = 0; + } + + template + inline void CircularLinkedList::Concatenate( const CircularLinkedList& L ) + { + unsigned int counter; + node* ptr; + + if ( L.list_size == 0 ) + return ; + + if ( this->list_size == 0 ) + * this = L; + + ptr = L.root; + + this->position = this->root->previous; + + // Cycle through each element in L and add it to the current list + for ( counter = 0; counter < L.list_size; counter++ ) + { + // Add item after the current item pointed to + // add(*(ptr->item)); + + Add ( ptr->item ); + + // Update pointers. Moving ptr keeps the current pointer at the end of the list since the add function does not move the pointer + ptr = ptr->next; + + this->position = this->position->next; + } + } + + template + inline void CircularLinkedList::Sort( void ) + { + if ( this->list_size <= 1 ) + return ; + + // Call equal operator to assign result of mergesort to current object + *this = Mergesort( *this ); + + this->position = this->root; + } + + template + CircularLinkedList CircularLinkedList::Mergesort( const CircularLinkedList& L ) + { + unsigned int counter; + node* location; + CircularLinkedList L1; + CircularLinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.list_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.list_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + CircularLinkedList CircularLinkedList::Merge( CircularLinkedList L1, CircularLinkedList L2 ) + { + CircularLinkedList X; + CircularLinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.list_size != 0 ) && ( L2.list_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + // element = *((L1.root)->item); + element = ( L1.root ) ->item; + L1.Del(); + } + else + { + // element = *((L2.root)->item); + element = ( L2.root ) ->item; + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + + X++; + } + + // Add the remaining list to X + if ( L1.list_size != 0 ) + X.Concatenate( L1 ); + else + X.Concatenate( L2 ); + + return X; + } + + template + LinkedList LinkedList::Mergesort( const LinkedList& L ) + { + unsigned int counter; + typename LinkedList::node* location; + LinkedList L1; + LinkedList L2; + + location = L.root; + + // Split the list into two equal size sublists, L1 and L2 + + for ( counter = 0; counter < L.LinkedList_size / 2; counter++ ) + { + // L1.add (*(location->item)); + L1.Add ( location->item ); + location = location->next; + } + + for ( ;counter < L.LinkedList_size; counter++ ) + { + // L2.Add(*(location->item)); + L2.Add ( location->item ); + location = location->next; + } + + // Recursively sort the sublists + if ( L1.list_size > 1 ) + L1 = Mergesort( L1 ); + + if ( L2.list_size > 1 ) + L2 = Mergesort( L2 ); + + // Merge the two sublists + return Merge( L1, L2 ); + } + + template + LinkedList LinkedList::Merge( LinkedList L1, LinkedList L2 ) + { + LinkedList X; + LinkedListType element; + L1.position = L1.root; + L2.position = L2.root; + + // While neither list is empty + + while ( ( L1.LinkedList_size != 0 ) && ( L2.LinkedList_size != 0 ) ) + { + // Compare the first items of L1 and L2 + // Remove the smaller of the two items from the list + + if ( ( ( L1.root ) ->item ) < ( ( L2.root ) ->item ) ) + // if ((*((L1.root)->item)) < (*((L2.root)->item))) + { + element = ( L1.root ) ->item; + // element = *((L1.root)->item); + L1.Del(); + } + else + { + element = ( L2.root ) ->item; + // element = *((L2.root)->item); + L2.Del(); + } + + // Add this item to the end of X + X.Add( element ); + } + + // Add the remaining list to X + if ( L1.LinkedList_size != 0 ) + X.concatenate( L1 ); + else + X.concatenate( L2 ); + + return X; + } + + + // Prefix + template + LinkedList& LinkedList::operator++() + { + if ( ( this->list_size != 0 ) && ( this->position->next != this->root ) ) + this->position = this->position->next; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator++(int) + { + LinkedList before; + before=*this; + operator++(); + return before; + } + */ + // Postfix + template + LinkedList& LinkedList::operator++( int ) + { + return this->operator++(); + } + + // Prefix + template + LinkedList& LinkedList::operator--() + { + if ( ( this->list_size != 0 ) && ( this->position != this->root ) ) + this->position = this->position->previous; + + return *this; + } + + /* + // Postfix + template + LinkedList& LinkedList::operator--(int) + { + LinkedList before; + before=*this; + operator--(); + return before; + } + */ + + // Postfix + template + LinkedList& LinkedList::operator--( int ) + { + return this->operator--(); + } + +} // End namespace + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/Multiplayer/raknet/DS_List.h b/Multiplayer/raknet/DS_List.h new file mode 100644 index 000000000..4e7c54d37 --- /dev/null +++ b/Multiplayer/raknet/DS_List.h @@ -0,0 +1,525 @@ +/// \file +/// \brief \b [Internal] Array based list. Usually the Queue class is used instead, since it has all the same functionality and is only worse at random access. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __LIST_H +#define __LIST_H + +#include "RakAssert.h" +#include // memmove +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Maximum unsigned long +static const unsigned int MAX_UNSIGNED_LONG = 4294967295U; + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief Array based implementation of a list. + /// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance. + template + class 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; + + /// 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& Get ( const unsigned int position ) const; + + /// Push an element at the end of the stack + /// \param[in] input The new element. + void Push(const list_type input); + + /// Pop an element from the end of the stack + /// \pre Size()>0 + /// \return The element at the end. + list_type& Pop(void); + + /// Insert an element at position \a position in the list + /// \param[in] input The new element. + /// \param[in] position The position of the new element. + void Insert( const list_type input, const unsigned int position ); + + /// Insert at the end of the list. + /// \param[in] input The new element. + void Insert( const list_type input ); + + /// Replace the value at \a position by \a input. If the size of + /// the list is less than @em position, it increase the capacity of + /// the list and fill slot with @em filler. + /// \param[in] input The element to replace at position @em position. + /// \param[in] filler The element use to fill new allocated capacity. + /// \param[in] position The position of input in the list. + void Replace( const list_type input, const list_type filler, const unsigned int position ); + + /// Replace the last element of the list by \a input . + /// \param[in] input The element used to replace the last element. + void Replace( const list_type input ); + + /// Delete the element at position \a position. + /// \param[in] position The index of the element to delete + void RemoveAtIndex( const unsigned int position ); + + /// Delete the element at position \a position. + /// \note - swaps middle with end of list, only use if list order does not matter + /// \param[in] position The index of the element to delete + void RemoveAtIndexFast( const unsigned int position ); + + /// Delete the element at the end of the list + void RemoveFromEnd(const unsigned num=1); + + /// Returns the index of the specified item or MAX_UNSIGNED_LONG if not found + /// \param[in] input The element to check for + /// \return The index or position of @em input in the list. + /// \retval MAX_UNSIGNED_LONG The object is not in the list + /// \retval [Integer] The index of the element in the list + unsigned int GetIndexOf( const list_type input ) const; + + /// \return The number of elements in the list + unsigned int Size( void ) const; + + /// Clear the list + void Clear( bool doNotDeallocateSmallBlocks=false ); + + // Preallocate the list, so it needs fewer reallocations at runtime + void Preallocate( unsigned countNeeded ); + + /// Frees overallocated members, to use the minimum memory necessary + /// \attention + /// This is a slow operation + void Compress( void ); + + private: + /// An array of user values + list_type* listArray; + + /// Number of elements in the list + unsigned int list_size; + + /// Size of \a array + unsigned int allocation_size; + }; + template + List::List() + { + allocation_size = 0; + listArray = 0; + list_size = 0; + } + + template + List::~List() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(listArray); + } + + + template + List::List( const List& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.list_size == 0 ) + { + list_size = 0; + allocation_size = 0; + } + else + { + listArray = RakNet::OP_NEW_ARRAY( 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 + List& List::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 = RakNet::OP_NEW_ARRAY( 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 + inline list_type& List::operator[] ( const unsigned int position ) const + { + #ifdef _DEBUG + if (position>=list_size) + { + RakAssert ( position < list_size ); + } + #endif + return listArray[ position ]; + } + + // Just here for debugging + template + inline list_type& List::Get ( const unsigned int position ) const + { + return listArray[ position ]; + } + + template + void List::Push(const list_type input) + { + Insert(input); + } + + template + inline list_type& List::Pop(void) + { +#ifdef _DEBUG + RakAssert(list_size>0); +#endif + --list_size; + return listArray[list_size]; + } + + template + void List::Insert( const list_type input, const unsigned int position ) + { +#ifdef _DEBUG + if (position>list_size) + { + RakAssert( 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 = RakNet::OP_NEW_ARRAY( 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 + RakNet::OP_DELETE_ARRAY(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 + void List::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 = RakNet::OP_NEW_ARRAY( allocation_size ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray); + } + + listArray = new_array; + } + + // Insert the new item at the correct spot + listArray[ list_size ] = input; + + ++list_size; + } + + template + inline void List::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 = RakNet::OP_NEW_ARRAY( 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 + RakNet::OP_DELETE_ARRAY(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 + + RakAssert( list_size == position + 1 ); + +#endif + + } + } + + template + inline void List::Replace( const list_type input ) + { + if ( list_size > 0 ) + listArray[ list_size - 1 ] = input; + } + + template + void List::RemoveAtIndex( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + + if ( position < list_size ) + { + // Compress the array + for ( unsigned int counter = position; counter < list_size - 1 ; ++counter ) + listArray[ counter ] = listArray[ counter + 1 ]; + // Don't call constructors, assignment operators, etc. + // memmove(listArray+position, listArray+position+1, (list_size-1-position) * sizeof(list_type)); + + RemoveFromEnd(); + } + } + + template + void List::RemoveAtIndexFast( const unsigned int position ) + { +#ifdef _DEBUG + if (position >= list_size) + { + RakAssert( position < list_size ); + return; + } +#endif + --list_size; + listArray[position]=listArray[list_size]; + } + + template + inline void List::RemoveFromEnd( const unsigned num ) + { + // Delete the last elements on the list. No compression needed +#ifdef _DEBUG + RakAssert(list_size>=num); +#endif + list_size-=num; + } + + template + unsigned int List::GetIndexOf( const list_type input ) const + { + for ( unsigned int i = 0; i < list_size; ++i ) + if ( listArray[ i ] == input ) + return i; + + return MAX_UNSIGNED_LONG; + } + + template + inline unsigned int List::Size( void ) const + { + return list_size; + } + + template + void List::Clear( bool doNotDeallocateSmallBlocks ) + { + if ( allocation_size == 0 ) + return; + + if (allocation_size>512 || doNotDeallocateSmallBlocks==false) + { + RakNet::OP_DELETE_ARRAY(listArray); + allocation_size = 0; + listArray = 0; + } + list_size = 0; + } + + template + void List::Compress( void ) + { + list_type * new_array; + + if ( allocation_size == 0 ) + return ; + + new_array = RakNet::OP_NEW_ARRAY( 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 + RakNet::OP_DELETE_ARRAY(listArray); + + listArray = new_array; + } + + template + void List::Preallocate( unsigned countNeeded ) + { + unsigned amountToAllocate = allocation_size; + if (allocation_size==0) + amountToAllocate=16; + while (amountToAllocate < countNeeded) + amountToAllocate<<=1; + + if ( allocation_size < amountToAllocate) + { + // allocate twice the currently allocated memory + list_type * new_array; + + allocation_size=amountToAllocate; + + new_array = RakNet::OP_NEW_ARRAY< list_type >( allocation_size ); + + if (listArray) + { + // copy old array over + for ( unsigned int counter = 0; counter < list_size; ++counter ) + new_array[ counter ] = listArray[ counter ]; + + // Don't call constructors, assignment operators, etc. + //memcpy(new_array, listArray, list_size*sizeof(list_type)); + + // set old array to point to the newly allocated and twice as large array + RakNet::OP_DELETE_ARRAY(listArray); + } + + listArray = new_array; + } + } + +} // End namespace + +#endif diff --git a/Multiplayer/raknet/DS_Map.h b/Multiplayer/raknet/DS_Map.h new file mode 100644 index 000000000..770556537 --- /dev/null +++ b/Multiplayer/raknet/DS_Map.h @@ -0,0 +1,330 @@ +/// \file +/// \brief \b [Internal] Map +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAKNET_MAP_H +#define __RAKNET_MAP_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "RakAssert.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 + int defaultMapKeyComparison(const key_type &a, const key_type &b) + { + if (a > + class RAK_DLL_EXPORT Map + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(key_type(),key_type());} + + struct MapNode + { + MapNode() {} + MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {} + MapNode& operator = ( const MapNode& input ) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData; return *this;} + MapNode( const MapNode & input) {mapNodeKey=input.mapNodeKey; mapNodeData=input.mapNodeData;} + key_type mapNodeKey; + data_type mapNodeData; + }; + + // Has to be a static because the comparison callback for DataStructures::OrderedList is a C function + static int NodeComparisonFunc(const key_type &a, const MapNode &b) + { +#ifdef _MSC_VER +#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + return key_comparison_func(a, b.mapNodeKey); + } + + Map(); + ~Map(); + Map( const Map& original_copy ); + Map& operator= ( const Map& original_copy ); + + data_type& Get(const key_type &key) const; + 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) const; + 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) const; + bool HasSavedSearchResult(const key_type &key) const; + + unsigned lastSearchIndex; + key_type lastSearchKey; + bool lastSearchIndexValid; + }; + + template + Map::Map() + { + lastSearchIndexValid=false; + } + + template + Map::~Map() + { + Clear(); + } + + template + Map::Map( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + } + + template + Map& Map::operator= ( const Map& original_copy ) + { + mapNodeList=original_copy.mapNodeList; + lastSearchIndex=original_copy.lastSearchIndex; + lastSearchKey=original_copy.lastSearchKey; + lastSearchIndexValid=original_copy.lastSearchIndexValid; + return *this; + } + + template + data_type& Map::Get(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return mapNodeList[lastSearchIndex].mapNodeData; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + return mapNodeList[index].mapNodeData; + } + + template + unsigned Map::GetIndexAtKey( const key_type &key ) + { + if (HasSavedSearchResult(key)) + return lastSearchIndex; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists==false) + { + RakAssert(objectExists); + } + SaveLastSearch(key,index); + return index; + } + + template + void Map::RemoveAtIndex(const unsigned index) + { + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + } + + template + data_type Map::Pop(const key_type &key) + { + bool objectExists; + unsigned index; + if (HasSavedSearchResult(key)) + index=lastSearchIndex; + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + } + data_type tmp = mapNodeList[index].mapNodeData; + mapNodeList.RemoveAtIndex(index); + lastSearchIndexValid=false; + return tmp; + } + + template + void Map::Set(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + mapNodeList[lastSearchIndex].mapNodeData=data; + return; + } + + index=mapNodeList.GetIndexFromKey(key, &objectExists); + + if (objectExists) + { + SaveLastSearch(key,index); + mapNodeList[index].mapNodeData=data; + } + else + { + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true)); + } + } + + template + void Map::SetExisting(const key_type &key, const data_type &data) + { + bool objectExists; + unsigned index; + + if (HasSavedSearchResult(key)) + { + index=lastSearchIndex; + } + else + { + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists); + SaveLastSearch(key,index); + } + + mapNodeList[index].mapNodeData=data; + } + + template + void Map::SetNew(const key_type &key, const data_type &data) + { +#ifdef _DEBUG + unsigned index; + bool objectExists; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + RakAssert(objectExists==false); +#endif + SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true)); + } + + template + bool Map::Has(const key_type &key) const + { + if (HasSavedSearchResult(key)) + return true; + + bool objectExists; + unsigned index; + index=mapNodeList.GetIndexFromKey(key, &objectExists); + if (objectExists) + SaveLastSearch(key,index); + return objectExists; + } + + template + bool Map::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 + void Map::Clear(void) + { + lastSearchIndexValid=false; + mapNodeList.Clear(); + } + + template + data_type& Map::operator[]( const unsigned int position ) const + { + return mapNodeList[position].mapNodeData; + } + + template + key_type Map::GetKeyAtIndex( const unsigned int position ) const + { + return mapNodeList[position].mapNodeKey; + } + + template + unsigned Map::Size(void) const + { + return mapNodeList.Size(); + } + + template + void Map::SaveLastSearch(const key_type &key, const unsigned index) const + { + (void) key; + (void) index; + + /* + lastSearchIndex=index; + lastSearchKey=key; + lastSearchIndexValid=true; + */ + } + + template + bool Map::HasSavedSearchResult(const key_type &key) const + { + (void) key; + + // Not threadsafe! + return false; + // return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0; + } +} + +#endif diff --git a/Multiplayer/raknet/DS_MemoryPool.h b/Multiplayer/raknet/DS_MemoryPool.h new file mode 100644 index 000000000..9c93540ec --- /dev/null +++ b/Multiplayer/raknet/DS_MemoryPool.h @@ -0,0 +1,285 @@ +#ifndef __MEMORY_POOL_H +#define __MEMORY_POOL_H + +#ifndef __APPLE__ +// Use stdlib and not malloc for compatibility +#include +#endif +#include "RakAssert.h" +#include "Export.h" + +#include "RakMemoryOverride.h" + +// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1 +#define DS_MEMORY_POOL_MAX_FREE_PAGES 4 + +// #define _DISABLE_MEMORY_POOL + +namespace DataStructures +{ + /// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors. + /// Contains a list of pages, each of which has an array of the user structures + template + class RAK_DLL_EXPORT MemoryPool + { + public: + struct Page; + struct MemoryWithPage + { + MemoryBlockType userMemory; + Page *parentPage; + }; + + struct Page + { + MemoryWithPage** availableStack; + int availableStackSize; + MemoryWithPage* block; + Page *next, *prev; + }; + + MemoryPool(); + ~MemoryPool(); + void SetPageSize(int size); // Defaults to 16384 + MemoryBlockType *Allocate(void); + void Release(MemoryBlockType *m); + void Clear(void); + + int GetAvailablePagesSize(void) const {return availablePagesSize;} + int GetUnavailablePagesSize(void) const {return unavailablePagesSize;} + int GetMemoryPoolPageSize(void) const {return memoryPoolPageSize;} + protected: + int BlocksPerPage(void) const; + void AllocateFirst(void); + bool InitPage(Page *page, Page *prev); + + // availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list + // unavailablePages are pages which are totally full, and from which we do not return new blocks. + // Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages + Page *availablePages, *unavailablePages; + int availablePagesSize, unavailablePagesSize; + int memoryPoolPageSize; + }; + + template + MemoryPool::MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + //AllocateFirst(); + availablePagesSize=0; + unavailablePagesSize=0; + memoryPoolPageSize=16384; +#endif + } + template + MemoryPool::~MemoryPool() + { +#ifndef _DISABLE_MEMORY_POOL + Clear(); +#endif + } + + template + void MemoryPool::SetPageSize(int size) + { + memoryPoolPageSize=size; + } + + template + MemoryBlockType* MemoryPool::Allocate(void) + { +#ifdef _DISABLE_MEMORY_POOL + return new MemoryBlockType; +#endif + + if (availablePagesSize>0) + { + MemoryBlockType *retVal; + Page *curPage; + curPage=availablePages; + retVal = (MemoryBlockType*) curPage->availableStack[--(curPage->availableStackSize)]; + if (curPage->availableStackSize==0) + { + --availablePagesSize; + availablePages=curPage->next; + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize++==0) + { + unavailablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=unavailablePages; + curPage->prev=unavailablePages->prev; + unavailablePages->prev->next=curPage; + unavailablePages->prev=curPage; + } + } + + RakAssert(availablePagesSize==0 || availablePages->availableStackSize>0); + return retVal; + } + + availablePages = (Page *) rakMalloc(sizeof(Page)); + if (availablePages==0) + return 0; + availablePagesSize=1; + if (InitPage(availablePages, availablePages)==false) + return 0; + RakAssert(availablePages->availableStackSize>1); + return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize]; + } + template + void MemoryPool::Release(MemoryBlockType *m) + { +#ifdef _DISABLE_MEMORY_POOL + RakNet::OP_DELETE(m); + return; +#endif + + // Find the page this block is in and return it. + Page *curPage; + MemoryWithPage *memoryWithPage = (MemoryWithPage*)m; + curPage=memoryWithPage->parentPage; + + if (curPage->availableStackSize==0) + { + // The page is in the unavailable list so move it to the available list + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + unavailablePagesSize--; + + // As this page is no longer totally empty, move it to the end of available pages + curPage->next->prev=curPage->prev; + curPage->prev->next=curPage->next; + + if (unavailablePagesSize>0 && curPage==unavailablePages) + unavailablePages=unavailablePages->next; + + if (availablePagesSize++==0) + { + availablePages=curPage; + curPage->next=curPage; + curPage->prev=curPage; + } + else + { + curPage->next=availablePages; + curPage->prev=availablePages->prev; + availablePages->prev->next=curPage; + availablePages->prev=curPage; + } + } + else + { + curPage->availableStack[curPage->availableStackSize++]=memoryWithPage; + + if (curPage->availableStackSize==BlocksPerPage() && + availablePagesSize>=DS_MEMORY_POOL_MAX_FREE_PAGES) + { + // After a certain point, just deallocate empty pages rather than keep them around + if (curPage==availablePages) + { + availablePages=curPage->next; + RakAssert(availablePages->availableStackSize>0); + } + curPage->prev->next=curPage->next; + curPage->next->prev=curPage->prev; + availablePagesSize--; + rakFree(curPage->availableStack); + rakFree(curPage->block); + rakFree(curPage); + } + } + } + template + void MemoryPool::Clear(void) + { +#ifdef _DISABLE_MEMORY_POOL + return; +#endif + Page *cur, *freed; + + if (availablePagesSize>0) + { + cur = availablePages; +#ifdef _MSC_VER +#pragma warning(disable:4127) // conditional expression is constant +#endif + while (true) + // do + { + rakFree(cur->availableStack); + rakFree(cur->block); + freed=cur; + cur=cur->next; + if (cur==availablePages) + { + rakFree(freed); + break; + } + rakFree(freed); + }// while(cur!=availablePages); + } + + if (unavailablePagesSize>0) + { + cur = unavailablePages; + while (1) + //do + { + rakFree(cur->availableStack); + rakFree(cur->block); + freed=cur; + cur=cur->next; + if (cur==unavailablePages) + { + rakFree(freed); + break; + } + rakFree(freed); + } // while(cur!=unavailablePages); + } + + availablePagesSize=0; + unavailablePagesSize=0; + } + template + int MemoryPool::BlocksPerPage(void) const + { + return memoryPoolPageSize / sizeof(MemoryWithPage); + } + template + bool MemoryPool::InitPage(Page *page, Page *prev) + { + int i=0; + const int bpp = BlocksPerPage(); + page->block=(MemoryWithPage*) rakMalloc(memoryPoolPageSize); + if (page->block==0) + return false; + page->availableStack=(MemoryWithPage**)rakMalloc(sizeof(MemoryWithPage*)*bpp); + if (page->availableStack==0) + { + rakFree(page->block); + return false; + } + MemoryWithPage *curBlock = page->block; + MemoryWithPage **curStack = page->availableStack; + while (i < bpp) + { + curBlock->parentPage=page; + curStack[i]=curBlock++; + i++; + } + page->availableStackSize=bpp; + page->next=availablePages; + page->prev=prev; + return true; + } +} + +#endif diff --git a/Multiplayer/raknet/DS_OrderedChannelHeap.h b/Multiplayer/raknet/DS_OrderedChannelHeap.h new file mode 100644 index 000000000..067289e54 --- /dev/null +++ b/Multiplayer/raknet/DS_OrderedChannelHeap.h @@ -0,0 +1,252 @@ +/// \file +/// \brief \b [Internal] Ordered Channel Heap . This is a heap where you add to it on multiple ordered channels, with each channel having a different weight. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H +#define __RAKNET_ORDERED_CHANNEL_HEAP_H + +#include "DS_Heap.h" +#include "DS_Map.h" +#include "DS_Queue.h" +#include "Export.h" +#include "RakAssert.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 RAK_DLL_EXPORT OrderedChannelHeap + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(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 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 map; + DataStructures::Heap heap; + void GreatestRandResult(void); + }; + + template + OrderedChannelHeap::OrderedChannelHeap() + { + } + + template + OrderedChannelHeap::~OrderedChannelHeap() + { + Clear(); + } + + template + void OrderedChannelHeap::Push(const channel_key_type &channelID, const heap_data_type &data) + { + PushAtHead(MAX_UNSIGNED_LONG, channelID, data); + } + + template + void OrderedChannelHeap::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 + void OrderedChannelHeap::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 + RakAssert(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 + heap_data_type OrderedChannelHeap::Pop(const unsigned startingIndex) + { + RakAssert(startingIndex < heap.Size()); + + QueueAndWeight *queueAndWeight=map.Get(heap[startingIndex].channel); + if (startingIndex!=0) + { + // Ugly - have to count in the heap how many nodes have the same channel, so we know where to delete from in the queue + unsigned indiceCount=0; + unsigned i; + for (i=0; i < startingIndex; i++) + if (channel_key_comparison_func(heap[i].channel,heap[startingIndex].channel)==0) + indiceCount++; + queueAndWeight->randResultQueue.RemoveAtIndex(indiceCount); + } + else + { + // TODO - ordered channel heap uses progressively lower values as items are inserted. But this won't give relative ordering among channels. I have to renormalize after every pop. + queueAndWeight->randResultQueue.Pop(); + } + + // Try to remove the channel after every pop, because doing so is not valid while there are elements in the list. + if (queueAndWeight->signalDeletion) + RemoveChannel(heap[startingIndex].channel); + + return heap.Pop(startingIndex).data; + } + + template + heap_data_type OrderedChannelHeap::Peek(const unsigned startingIndex) const + { + HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex); + return heapChannelAndData.data; + } + + template + void OrderedChannelHeap::AddChannel(const channel_key_type &channelID, const double weight) + { + QueueAndWeight *qaw = RakNet::OP_NEW(); + qaw->weight=weight; + qaw->signalDeletion=false; + map.SetNew(channelID, qaw); + } + + template + void OrderedChannelHeap::RemoveChannel(channel_key_type channelID) + { + if (map.Has(channelID)) + { + unsigned i; + i=map.GetIndexAtKey(channelID); + if (map[i]->randResultQueue.Size()==0) + { + RakNet::OP_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 + unsigned OrderedChannelHeap::Size(void) const + { + return heap.Size(); + } + + template + heap_data_type& OrderedChannelHeap::operator[]( const unsigned int position ) const + { + return heap[position].data; + } + + + template + unsigned OrderedChannelHeap::ChannelSize(const channel_key_type &channelID) + { + QueueAndWeight *queueAndWeight=map.Get(channelID); + return queueAndWeight->randResultQueue.Size(); + } + + template + void OrderedChannelHeap::Clear(void) + { + unsigned i; + for (i=0; i < map.Size(); i++) + RakNet::OP_DELETE(map[i]); + map.Clear(); + heap.Clear(); + } +} + +#endif diff --git a/Multiplayer/raknet/DS_OrderedList.h b/Multiplayer/raknet/DS_OrderedList.h new file mode 100644 index 000000000..662b7486e --- /dev/null +++ b/Multiplayer/raknet/DS_OrderedList.h @@ -0,0 +1,272 @@ +/// \file +/// \brief \b [Internal] Quicksort ordered list. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +#ifndef __ORDERED_LIST_H +#define __ORDERED_LIST_H + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + int defaultOrderedListComparison(const key_type &a, const data_type &b) + { + if (a > + class RAK_DLL_EXPORT OrderedList + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison(key_type(),data_type());} + + OrderedList(); + ~OrderedList(); + OrderedList( const OrderedList& original_copy ); + OrderedList& operator= ( const OrderedList& original_copy ); + + /// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0 + /// If the data type has comparison operators already defined then you can just use defaultComparison + bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + // GetIndexFromKey returns where the insert should go at the same time checks if it is there + unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const; + unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + unsigned RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function); + data_type& operator[] ( const unsigned int position ) const; + void RemoveAtIndex(const unsigned index); + void InsertAtIndex(const data_type &data, const unsigned index); + void InsertAtEnd(const data_type &data); + void RemoveFromEnd(const unsigned num=1); + void Clear(bool doNotDeallocate=false); + unsigned Size(void) const; + + protected: + DataStructures::List orderedList; + }; + + template + OrderedList::OrderedList() + { + } + + template + OrderedList::~OrderedList() + { + Clear(); + } + + template + OrderedList::OrderedList( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + } + + template + OrderedList& OrderedList::operator= ( const OrderedList& original_copy ) + { + orderedList=original_copy.orderedList; + return *this; + } + + template + bool OrderedList::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + return objectExists; + } + + template + data_type OrderedList::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + RakAssert(objectExists); + return orderedList[index]; + } + template + bool OrderedList::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + if (objectExists) + element = orderedList[index]; + return objectExists; + } + template + unsigned OrderedList::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const + { + int index, upperBound, lowerBound; + int res; + + if (orderedList.Size()==0) + { + *objectExists=false; + return 0; + } + + upperBound=(int)orderedList.Size()-1; + lowerBound=0; + index = (int)orderedList.Size()/2; + +#ifdef _MSC_VER + #pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant +#endif + while (1) + { + res = cf(key,orderedList[index]); + if (res==0) + { + *objectExists=true; + return index; + } + else if (res<0) + { + upperBound=index-1; + } + else// if (res>0) + { + lowerBound=index+1; + } + + index=lowerBound+(upperBound-lowerBound)/2; + + if (lowerBound>upperBound) + { + *objectExists=false; + return lowerBound; // No match + } + } + } + + template + unsigned OrderedList::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, int (*cf)(const key_type&, const data_type&)) + { + (void) assertOnDuplicate; + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Don't allow duplicate insertion. + if (objectExists) + { + // This is usually a bug! Use InsertAllowDuplicate if you want duplicates + RakAssert(assertOnDuplicate==false); + return (unsigned)-1; + } + + if (index>=orderedList.Size()) + { + orderedList.Insert(data); + return orderedList.Size()-1; + } + else + { + orderedList.Insert(data,index); + return index; + } + } + + template + unsigned OrderedList::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + // RakAssert(objectExists==true); + if (objectExists==false) + { + RakAssert(objectExists==true); + return 0; + } + + orderedList.RemoveAtIndex(index); + return index; + } + + template + unsigned OrderedList::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&)) + { + bool objectExists; + unsigned index; + index = GetIndexFromKey(key, &objectExists, cf); + + // Can't find the element to remove if this assert hits + if (objectExists==false) + return 0; + + orderedList.RemoveAtIndex(index); + return index; + } + + template + void OrderedList::RemoveAtIndex(const unsigned index) + { + orderedList.RemoveAtIndex(index); + } + + template + void OrderedList::InsertAtIndex(const data_type &data, const unsigned index) + { + orderedList.Insert(data, index); + } + + template + void OrderedList::InsertAtEnd(const data_type &data) + { + orderedList.Insert(data); + } + + template + void OrderedList::RemoveFromEnd(const unsigned num) + { + orderedList.RemoveFromEnd(num); + } + + template + void OrderedList::Clear(bool doNotDeallocate) + { + orderedList.Clear(doNotDeallocate); + } + + template + data_type& OrderedList::operator[]( const unsigned int position ) const + { + return orderedList[position]; + } + + template + unsigned OrderedList::Size(void) const + { + return orderedList.Size(); + } +} + +#endif diff --git a/Multiplayer/raknet/DS_Queue.h b/Multiplayer/raknet/DS_Queue.h new file mode 100644 index 000000000..5b7960cfb --- /dev/null +++ b/Multiplayer/raknet/DS_Queue.h @@ -0,0 +1,420 @@ +/// \file +/// \brief \b [Internal] A queue used by RakNet. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __QUEUE_H +#define __QUEUE_H + +// Template classes have to have all the code in the header file +#include "RakAssert.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented as an array with a read and write index. + template + class RAK_DLL_EXPORT Queue + { + public: + Queue(); + ~Queue(); + Queue( Queue& original_copy ); + bool operator= ( const Queue& original_copy ); + void Push( const queue_type& input ); + void PushAtHead( const queue_type& input, unsigned index=0 ); + queue_type& operator[] ( unsigned int position ) const; // Not a normal thing you do with a queue but can be used for efficiency + void RemoveAtIndex( unsigned int position ); // Not a normal thing you do with a queue but can be used for efficiency + inline queue_type Peek( void ) const; + inline queue_type PeekTail( void ) const; + inline queue_type Pop( void ); + inline unsigned int Size( void ) const; + inline bool IsEmpty(void) const; + inline unsigned int AllocationSize( void ) const; + inline void Clear( void ); + void Compress( void ); + bool Find ( queue_type q ); + void ClearAndForceAllocation( int size ); // Force a memory allocation to a certain larger size + + private: + queue_type* array; + unsigned int head; // Array index for the head of the queue + unsigned int tail; // Array index for the tail of the queue + unsigned int allocation_size; + }; + + + template + inline unsigned int Queue::Size( void ) const + { + if ( head <= tail ) + return tail -head; + else + return allocation_size -head + tail; + } + + template + inline bool Queue::IsEmpty(void) const + { + return head==tail; + } + + template + inline unsigned int Queue::AllocationSize( void ) const + { + return allocation_size; + } + + template + Queue::Queue() + { + allocation_size = 16; + //array = RakNet::PLACEMENT_NEW(allocation_size); + array = RakNet::OP_NEW_ARRAY(allocation_size); + head = 0; + tail = 0; + } + + template + Queue::~Queue() + { + if (allocation_size>0) + RakNet::OP_DELETE_ARRAY(array); + } + + template + inline queue_type Queue::Pop( void ) + { +#ifdef _DEBUG + RakAssert( 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 + void Queue::PushAtHead( const queue_type& input, unsigned index ) + { + RakAssert(index <= Size()); + + // Just force a reallocation, will be overwritten + Push(input); + + if (Size()==1) + return; + + unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex; + writeIndex=Size()-1; + readIndex=writeIndex-1; + while (readIndex >= index) + { + if ( head + writeIndex >= allocation_size ) + trueWriteIndex = head + writeIndex - allocation_size; + else + trueWriteIndex = head + writeIndex; + + if ( head + readIndex >= allocation_size ) + trueReadIndex = head + readIndex - allocation_size; + else + trueReadIndex = head + readIndex; + + array[trueWriteIndex]=array[trueReadIndex]; + + if (readIndex==0) + break; + writeIndex--; + readIndex--; + } + + if ( head + index >= allocation_size ) + trueWriteIndex = head + index - allocation_size; + else + trueWriteIndex = head + index; + + array[trueWriteIndex]=input; + } + + + template + inline queue_type Queue::Peek( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); + RakAssert( allocation_size > 0 && Size() >= 0 ); +#endif + + return ( queue_type ) array[ head ]; + } + + template + inline queue_type Queue::PeekTail( void ) const + { +#ifdef _DEBUG + RakAssert( head != tail ); + RakAssert( allocation_size > 0 && Size() >= 0 ); +#endif + if (tail!=0) + return ( queue_type ) array[ tail-1 ]; + else + return ( queue_type ) array[ allocation_size-1 ]; + } + + template + void Queue::Push( const queue_type& input ) + { + if ( allocation_size == 0 ) + { + array = RakNet::OP_NEW_ARRAY(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 = RakNet::OP_NEW_ARRAY(allocation_size * 2); +#ifdef _DEBUG + RakAssert( new_array ); +#endif + if (new_array==0) + return; + + for ( unsigned int counter = 0; counter < allocation_size; ++counter ) + new_array[ counter ] = array[ ( head + counter ) % ( allocation_size ) ]; + + head = 0; + + tail = allocation_size; + + allocation_size *= 2; + + // Delete the old array and move the pointer to the new array + RakNet::OP_DELETE_ARRAY(array); + + array = new_array; + } + + } + + template + Queue::Queue( Queue& original_copy ) + { + // Allocate memory for copy + + if ( original_copy.Size() == 0 ) + { + allocation_size = 0; + } + + else + { + array = RakNet::OP_NEW_ARRAY( 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 + bool Queue::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 = RakNet::OP_NEW_ARRAY( 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 + inline void Queue::Clear ( void ) + { + if ( allocation_size == 0 ) + return ; + + if (allocation_size > 32) + { + RakNet::OP_DELETE_ARRAY(array); + allocation_size = 0; + } + + head = 0; + tail = 0; + } + + template + void Queue::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 = RakNet::OP_NEW_ARRAY(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 + RakNet::OP_DELETE_ARRAY(array); + array=new_array; + } + + template + bool Queue::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 + void Queue::ClearAndForceAllocation( int size ) + { + RakNet::OP_DELETE_ARRAY(array); + array = RakNet::OP_NEW_ARRAY(size); + allocation_size = size; + head = 0; + tail = 0; + } + + template + inline queue_type& Queue::operator[] ( unsigned int position ) const + { +#ifdef _DEBUG + RakAssert( 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 + void Queue::RemoveAtIndex( unsigned int position ) + { +#ifdef _DEBUG + RakAssert( position < Size() ); + RakAssert( 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 + diff --git a/Multiplayer/raknet/DS_QueueLinkedList.h b/Multiplayer/raknet/DS_QueueLinkedList.h new file mode 100644 index 000000000..bd4ab8c1a --- /dev/null +++ b/Multiplayer/raknet/DS_QueueLinkedList.h @@ -0,0 +1,111 @@ +/// \file +/// \brief \b [Internal] A queue implemented as a linked list. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __QUEUE_LINKED_LIST_H +#define __QUEUE_LINKED_LIST_H + +#include "DS_LinkedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A queue implemented using a linked list. Rarely used. + template + class 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 data; + }; + + template + QueueLinkedList::QueueLinkedList() + { + } + + template + inline unsigned int QueueLinkedList::Size() + { + return data.Size(); + } + + template + inline QueueType QueueLinkedList::Pop( void ) + { + data.Beginning(); + return ( QueueType ) data.Pop(); + } + + template + inline QueueType& QueueLinkedList::Peek( void ) + { + data.Beginning(); + return ( QueueType ) data.Peek(); + } + + template + inline QueueType& QueueLinkedList::EndPeek( void ) + { + data.End(); + return ( QueueType ) data.Peek(); + } + + template + void QueueLinkedList::Push( const QueueType& input ) + { + data.End(); + data.Add( input ); + } + + template + QueueLinkedList::QueueLinkedList( const QueueLinkedList& original_copy ) + { + data = original_copy.data; + } + + template + bool QueueLinkedList::operator= ( const QueueLinkedList& original_copy ) + { + if ( ( &original_copy ) == this ) + return false; + + data = original_copy.data; + } + + template + void QueueLinkedList::Clear ( void ) + { + data.Clear(); + } +} // End namespace + +#endif diff --git a/Multiplayer/raknet/DS_RangeList.h b/Multiplayer/raknet/DS_RangeList.h new file mode 100644 index 000000000..737f823e4 --- /dev/null +++ b/Multiplayer/raknet/DS_RangeList.h @@ -0,0 +1,219 @@ +#ifndef __RANGE_LIST_H +#define __RANGE_LIST_H + +#include "DS_OrderedList.h" +#include "BitStream.h" +#include "RakMemoryOverride.h" +#include "RakAssert.h" + +namespace DataStructures +{ + template + struct RangeNode + { + RangeNode() {} + ~RangeNode() {} + RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;} + range_type minIndex; + range_type maxIndex; + }; + + + template + int RangeNodeComp(const range_type &a, const RangeNode &b) + { + if (a + class RAK_DLL_EXPORT RangeList + { + public: + RangeList(); + ~RangeList(); + void Insert(range_type index); + void Clear(void); + unsigned Size(void) const; + unsigned RangeSum(void) const; + BitSize_t Serialize(RakNet::BitStream *in, BitSize_t maxBits, bool clearSerialized); + bool Deserialize(RakNet::BitStream *out); + + DataStructures::OrderedList , RangeNodeComp > ranges; + }; + + template + BitSize_t RangeList::Serialize(RakNet::BitStream *in, BitSize_t maxBits, bool clearSerialized) + { + RakAssert(ranges.Size() < (unsigned short)-1); + RakNet::BitStream tempBS; + BitSize_t bitsWritten; + unsigned short countWritten; + unsigned i; + countWritten=0; + bitsWritten=0; + for (i=0; i < ranges.Size(); i++) + { + if ((int)sizeof(unsigned short)*8+bitsWritten+(int)sizeof(range_type)*8*2+1>maxBits) + break; + tempBS.Write(ranges[i].minIndex==ranges[i].maxIndex); + tempBS.Write(ranges[i].minIndex); + bitsWritten+=sizeof(range_type)*8+1; + if (ranges[i].minIndex!=ranges[i].maxIndex) + { + tempBS.Write(ranges[i].maxIndex); + bitsWritten+=sizeof(range_type)*8; + } + countWritten++; + } + + BitSize_t before=in->GetWriteOffset(); + in->WriteCompressed(countWritten); + bitsWritten+=in->GetWriteOffset()-before; + // RAKNET_DEBUG_PRINTF("%i ", in->GetNumberOfBitsUsed()); + in->Write(&tempBS, tempBS.GetNumberOfBitsUsed()); + // RAKNET_DEBUG_PRINTF("%i %i \n", tempBS.GetNumberOfBitsUsed(),in->GetNumberOfBitsUsed()); + + if (clearSerialized && countWritten) + { + unsigned rangeSize=ranges.Size(); + for (i=0; i < rangeSize-countWritten; i++) + { + ranges[i]=ranges[i+countWritten]; + } + ranges.RemoveFromEnd(countWritten); + } + + return bitsWritten; + } + template + bool RangeList::Deserialize(RakNet::BitStream *out) + { + ranges.Clear(); + unsigned short count; + out->ReadCompressed(count); + unsigned short i; + range_type min,max; + bool maxEqualToMin=false; + + for (i=0; i < count; i++) + { + out->Read(maxEqualToMin); + if (out->Read(min)==false) + return false; + if (maxEqualToMin==false) + { + if (out->Read(max)==false) + return false; + if (max(min,max)); + } + return true; + } + + template + RangeList::RangeList() + { + RangeNodeComp(0, RangeNode()); + } + + template + RangeList::~RangeList() + { + Clear(); + } + + template + void RangeList::Insert(range_type index) + { + if (ranges.Size()==0) + { + ranges.Insert(index, RangeNode(index, index), true); + return; + } + + bool objectExists; + unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists); + if (insertionIndex==ranges.Size()) + { + if (index == ranges[insertionIndex-1].maxIndex+1) + ranges[insertionIndex-1].maxIndex++; + else if (index > ranges[insertionIndex-1].maxIndex+1) + { + // Insert at end + ranges.Insert(index, RangeNode(index, index), true); + } + + return; + } + + if (index < ranges[insertionIndex].minIndex-1) + { + // Insert here + ranges.InsertAtIndex(RangeNode(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 + void RangeList::Clear(void) + { + ranges.Clear(); + } + + template + unsigned RangeList::Size(void) const + { + return ranges.Size(); + } + + template + unsigned RangeList::RangeSum(void) const + { + unsigned sum=0,i; + for (i=0; i < ranges.Size(); i++) + sum+=ranges[i].maxIndex-ranges[i].minIndex+1; + return sum; + } + +} + +#endif diff --git a/Multiplayer/raknet/DS_Table.h b/Multiplayer/raknet/DS_Table.h new file mode 100644 index 000000000..c631a0702 --- /dev/null +++ b/Multiplayer/raknet/DS_Table.h @@ -0,0 +1,331 @@ +#ifndef __TABLE_H +#define __TABLE_H + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +#include "DS_List.h" +#include "DS_BPlusTree.h" +#include "RakMemoryOverride.h" +#include "Export.h" +#include "RakString.h" + +#define _TABLE_BPLUS_TREE_ORDER 16 +#define _TABLE_MAX_COLUMN_NAME_LENGTH 64 + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + + /// \brief Holds a set of columns, a set of rows, and rows times columns cells. + /// The table data structure is useful if you want to store a set of structures and perform queries on those structures + /// This is a relatively simple and fast implementation of the types of tables commonly used in databases + /// See TableSerializer to serialize data members of the table + /// See LightweightDatabaseClient and LightweightDatabaseServer to transmit the table over the network. + class RAK_DLL_EXPORT Table + { + public: + + enum ColumnType + { + // Cell::i used + NUMERIC, + + // Cell::c used to hold a null terminated string. + STRING, + + // Cell::c holds data. Cell::i holds data length of c in bytes. + BINARY, + + // Cell::c holds data. Not deallocated. Set manually by assigning ptr. + POINTER, + }; + + + /// Holds the actual data in the table + struct RAK_DLL_EXPORT Cell + { + Cell(); + ~Cell(); + Cell(double numericValue, char *charValue, void *ptr, ColumnType type); + void Clear(void); + + /// Numeric + void Set(int input); + void Set(unsigned int input); + void Set(double input); + + /// String + void Set(const char *input); + + /// Binary + void Set(const char *input, int inputLength); + + /// Pointer + void SetPtr(void* p); + + /// Numeric + void Get(int *output); + void Get(double *output); + + /// String + void Get(char *output); + + /// Binary + void Get(char *output, int *outputLength); + + RakNet::RakString ToString(ColumnType columnType); + + // assignment operator and copy constructor + Cell& operator = ( const Cell& input ); + Cell( const Cell & input); + + ColumnType EstimateColumnType(void) const; + + bool isEmpty; + double i; + char *c; + void *ptr; + }; + + /// Stores the name and type of the column + /// \internal + struct RAK_DLL_EXPORT ColumnDescriptor + { + ColumnDescriptor(); + ~ColumnDescriptor(); + ColumnDescriptor(const char cn[_TABLE_MAX_COLUMN_NAME_LENGTH],ColumnType ct); + + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + ColumnType columnType; + }; + + /// Stores the list of cells for this row, and a special flag used for internal sorting + struct RAK_DLL_EXPORT Row + { + // list of cells + DataStructures::List cells; + + /// Numeric + void UpdateCell(unsigned columnIndex, double value); + + /// String + void UpdateCell(unsigned columnIndex, const char *str); + + /// Binary + void UpdateCell(unsigned columnIndex, int byteLength, const char *data); + }; + + // Operations to perform for cell comparison + enum FilterQueryType + { + QF_EQUAL, + QF_NOT_EQUAL, + QF_GREATER_THAN, + QF_GREATER_THAN_EQ, + QF_LESS_THAN, + QF_LESS_THAN_EQ, + QF_IS_EMPTY, + QF_NOT_EMPTY, + }; + + // Compare the cell value for a row at columnName to the cellValue using operation. + struct RAK_DLL_EXPORT FilterQuery + { + FilterQuery(); + ~FilterQuery(); + FilterQuery(unsigned column, Cell *cell, FilterQueryType op); + + // If columnName is specified, columnIndex will be looked up using it. + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; + unsigned columnIndex; + Cell *cellValue; + FilterQueryType operation; + }; + + /// Increasing or decreasing sort order + enum SortQueryType + { + QS_INCREASING_ORDER, + QS_DECREASING_ORDER, + }; + + // Sort on increasing or decreasing order for a particular column + struct RAK_DLL_EXPORT SortQuery + { + /// The index of the table column we are sorting on + unsigned columnIndex; + + /// See SortQueryType + SortQueryType operation; + }; + + /// Constructor + Table(); + + /// Destructor + ~Table(); + + /// \brief Adds a column to the table + /// \param[in] columnName The name of the column + /// \param[in] columnType What type of data this column will hold + /// \return The index of the new column + unsigned AddColumn(const char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH], ColumnType columnType); + + /// \brief Removes a column by index + /// \param[in] columnIndex The index of the column to remove + void RemoveColumn(unsigned columnIndex); + + /// \brief Gets the index of a column by name + /// Column indices are stored in the order they are added. + /// \param[in] columnName The name of the column + /// \return The index of the column, or (unsigned)-1 if no such column + unsigned ColumnIndex(char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]); + unsigned ColumnIndex(const char *columnName); + + /// \brief Gives the string name of the column at a certain index + /// \param[in] index The index of the column + /// \return The name of the column, or 0 if an invalid index + char* ColumnName(unsigned index) const; + + /// \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) const; + + /// 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 &initialCellValues); + Table::Row* AddRow(unsigned rowId, DataStructures::List &initialCellValues, bool copyCells=false); + + /// Removes a row specified by rowId + /// \param[in] rowId The ID of the row + /// \return true if the row was deleted. False if not. + bool RemoveRow(unsigned rowId); + + /// Removes all the rows with IDs that the specified table also has + /// \param[in] tableContainingRowIDs The IDs of the rows + void RemoveRows(Table *tableContainingRowIDs); + + /// Updates a particular cell in the table + /// \note If you are going to update many cells of a particular row, it is more efficient to call GetRow and perform the operations on the row directly. + /// \note Row pointers do not change, so you can also write directly to the rows for more efficiency. + /// \param[in] rowId The ID of the row + /// \param[in] columnIndex The column of the cell + /// \param[in] value The data to set + bool UpdateCell(unsigned rowId, unsigned columnIndex, int value); + bool UpdateCell(unsigned rowId, unsigned columnIndex, char *str); + bool UpdateCell(unsigned rowId, unsigned columnIndex, int byteLength, char *data); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int value); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, char *str); + bool UpdateCellByIndex(unsigned rowIndex, unsigned columnIndex, int byteLength, char *data); + + /// Note this is much less efficient to call than GetRow, then working with the cells directly + /// Numeric, string, binary + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, int *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output); + void GetCellValueByIndex(unsigned rowIndex, unsigned columnIndex, char *output, int *outputLength); + + /// Gets a row. More efficient to do this and access Row::cells than to repeatedly call GetCell. + /// You can also update cells in rows from this function. + /// \param[in] rowId The ID of the row + /// \return The desired row, or 0 if no such row. + Row* GetRowByID(unsigned rowId) const; + + /// Gets a row at a specific index + /// rowIndex should be less than GetRowCount() + /// \param[in] rowIndex The index of the row + /// \param[out] key The ID of the row returned + /// \return The desired row, or 0 if no such row. + Row* GetRowByIndex(unsigned rowIndex, unsigned *key) const; + + /// \brief Queries the table, optionally returning only a subset of columns and rows. + /// \param[in] columnSubset An array of column indices. Only columns in this array are returned. Pass 0 for all columns + /// \param[in] numColumnSubset The number of elements in \a columnSubset + /// \param[in] inclusionFilters An array of FilterQuery. All filters must pass for the row to be returned. + /// \param[in] numInclusionFilters The number of elements in \a inclusionFilters + /// \param[in] rowIds An arrow of row IDs. Only these rows with these IDs are returned. Pass 0 for all rows. + /// \param[in] numRowIDs The number of elements in \a rowIds + /// \param[out] result The result of the query. If no rows are returned, the table will only have columns. + void QueryTable(unsigned *columnIndicesSubset, unsigned numColumnSubset, FilterQuery *inclusionFilters, unsigned numInclusionFilters, unsigned *rowIds, unsigned numRowIDs, Table *result); + + /// \brief Sorts the table by rows + /// You can sort the table in ascending or descending order on one or more columns + /// Columns have precedence in the order they appear in the \a sortQueries array + /// If a row cell on column n has the same value as a a different row on column n, then the row will be compared on column n+1 + /// \param[in] sortQueries A list of SortQuery structures, defining the sorts to perform on the table + /// \param[in] numColumnSubset The number of elements in \a numSortQueries + /// \param[out] out The address of an array of Rows, which will receive the sorted output. The array must be long enough to contain all returned rows, up to GetRowCount() + void SortTable(Table::SortQuery *sortQueries, unsigned numSortQueries, Table::Row** out); + + /// Frees all memory in the table. + void Clear(void); + + /// Prints out the names of all the columns + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + void PrintColumnHeaders(char *out, int outLength, char columnDelineator) const; + + /// Writes a text representation of the row to \a out + /// \param[out] out A pointer to an array of bytes which will hold the output. + /// \param[in] outLength The size of the \a out array + /// \param[in] columnDelineator What character to print to delineate columns + /// \param[in] printDelineatorForBinary Binary output is not printed. True to still print the delineator. + /// \param[in] inputRow The row to print + void PrintRow(char *out, int outLength, char columnDelineator, bool printDelineatorForBinary, Table::Row* inputRow) const; + + /// Direct access to make things easier + DataStructures::List& GetColumns(void); + + /// Direct access to make things easier + const DataStructures::BPlusTree& GetRows(void) const; + + /// Get the head of a linked list containing all the row data + DataStructures::Page * GetListHead(void); + + /// Get the first free row id. + /// This could be made more efficient. + unsigned GetAvailableRowId(void) const; + + Table& operator = ( const Table& input ); + + protected: + Table::Row* AddRowColumns(unsigned rowId, Row *row, DataStructures::List columnIndices); + + void DeleteRow(Row *row); + + void QueryRow(DataStructures::List &inclusionFilterColumnIndices, DataStructures::List &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 rows; + + // Columns in the table. + DataStructures::List columns; + }; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/Multiplayer/raknet/DS_Tree.h b/Multiplayer/raknet/DS_Tree.h new file mode 100644 index 000000000..d65f3d78a --- /dev/null +++ b/Multiplayer/raknet/DS_Tree.h @@ -0,0 +1,98 @@ +/// \file +/// \brief \b [Internal] Just a regular tree +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __DS_TREE_H +#define __DS_TREE_H + +#include "Export.h" +#include "DS_List.h" +#include "DS_Queue.h" +#include "RakMemoryOverride.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + template + class RAK_DLL_EXPORT Tree + { + public: + Tree(); + Tree(TreeType &inputData); + ~Tree(); + void LevelOrderTraversal(DataStructures::List &output); + void AddChild(TreeType &newData); + void DeleteDecendants(void); + + TreeType data; + DataStructures::List children; + }; + + template + Tree::Tree() + { + + } + + template + Tree::Tree(TreeType &inputData) + { + data=inputData; + } + + template + Tree::~Tree() + { + } + + template + void Tree::LevelOrderTraversal(DataStructures::List &output) + { + unsigned i; + Tree *node; + DataStructures::Queue*> 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 + void Tree::AddChild(TreeType &newData) + { + children.Insert(new Tree(newData)); + } + + template + void Tree::DeleteDecendants(void) + { + DataStructures::List output; + LevelOrderTraversal(output); + unsigned i; + for (i=0; i < output.Size(); i++) + RakNet::OP_DELETE(output[i]); + } +} + +#endif diff --git a/Multiplayer/raknet/DS_WeightedGraph.h b/Multiplayer/raknet/DS_WeightedGraph.h new file mode 100644 index 000000000..48a6d45be --- /dev/null +++ b/Multiplayer/raknet/DS_WeightedGraph.h @@ -0,0 +1,544 @@ +/// \file +/// \brief \b [Internal] Weighted graph. I'm assuming the indices are complex map types, rather than sequential numbers (which could be implemented much more efficiently). +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __WEIGHTED_GRAPH_H +#define __WEIGHTED_GRAPH_H + +#include "DS_OrderedList.h" +#include "DS_Map.h" +#include "DS_Heap.h" +#include "DS_Queue.h" +#include "DS_Tree.h" +#include "RakAssert.h" +#include "RakMemoryOverride.h" +#ifdef _DEBUG +#include +#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 RAK_DLL_EXPORT WeightedGraph + { + public: + static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison(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 &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT); + bool GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *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 *> adjacencyLists; + + // All these variables are for path finding with Dijkstra + // 08/23/06 Won't compile as a DLL inside this struct + // struct + // { + bool isValidPath; + node_type rootNode; + DataStructures::OrderedList costMatrixIndices; + weight_type *costMatrix; + node_type *leastNodeArray; + // } dijkstra; + + struct NodeAndParent + { + DataStructures::Tree*node; + DataStructures::Tree*parent; + }; + }; + + template + WeightedGraph::WeightedGraph() + { + isValidPath=false; + costMatrix=0; + } + + template + WeightedGraph::~WeightedGraph() + { + Clear(); + } + + template + WeightedGraph::WeightedGraph( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size()); + leastNodeArray = RakNet::OP_NEW_ARRAY(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 + WeightedGraph& WeightedGraph::operator=( const WeightedGraph& original_copy ) + { + adjacencyLists=original_copy.adjacencyLists; + + isValidPath=original_copy.isValidPath; + if (isValidPath) + { + rootNode=original_copy.rootNode; + costMatrixIndices=original_copy.costMatrixIndices; + costMatrix = RakNet::OP_NEW_ARRAY(costMatrixIndices.Size() * costMatrixIndices.Size()); + leastNodeArray = RakNet::OP_NEW_ARRAY(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 + void WeightedGraph::AddNode(const node_type &node) + { + adjacencyLists.SetNew(node, RakNet::OP_NEW >()); + } + + template + void WeightedGraph::RemoveNode(const node_type &node) + { + unsigned i; + DataStructures::Queue removeNodeQueue; + + removeNodeQueue.Push(node); + while (removeNodeQueue.Size()) + { + RakNet::OP_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 + bool WeightedGraph::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 + void WeightedGraph::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 + void WeightedGraph::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 + void WeightedGraph::Clear(void) + { + unsigned i; + for (i=0; i < adjacencyLists.Size(); i++) + RakNet::OP_DELETE(adjacencyLists[i]); + adjacencyLists.Clear(); + + ClearDijkstra(); + } + + template + bool WeightedGraph::GetShortestPath(DataStructures::List &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT) + { + path.Clear(); + if (startNode==endNode) + { + path.Insert(startNode); + path.Insert(endNode); + return true; + } + + if (isValidPath==false || rootNode!=startNode) + { + ClearDijkstra(); + GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT); + } + + // return the results + bool objectExists; + unsigned col,row; + weight_type currentWeight; + DataStructures::Queue 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 + node_type WeightedGraph::GetNodeAtIndex(unsigned nodeIndex) const + { + return adjacencyLists.GetKeyAtIndex(nodeIndex); + } + + template + unsigned WeightedGraph::GetNodeCount(void) const + { + return adjacencyLists.Size(); + } + + template + unsigned WeightedGraph::GetConnectionCount(unsigned nodeIndex) const + { + return adjacencyLists[nodeIndex]->Size(); + } + + template + void WeightedGraph::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const + { + outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex); + outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex); + } + + template + bool WeightedGraph::GetSpanningTree(DataStructures::Tree &outTree, DataStructures::List *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 path; + DataStructures::WeightedGraph 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 nodesToProcess; + DataStructures::Tree *current; + DataStructures::Map *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=RakNet::OP_NEW >(); + 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=RakNet::OP_NEW >(); + nap2.node->data=key; + nap2.parent=current; + nodesToProcess.Push(nap2); + current->children.Insert(nap2.node); + } + } + } + + return true; + } + + template + void WeightedGraph::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT) + { + if (adjacencyLists.Size()==0) + return; + + costMatrix = RakNet::OP_NEW_ARRAY(adjacencyLists.Size() * adjacencyLists.Size()); + leastNodeArray = RakNet::OP_NEW_ARRAY(adjacencyLists.Size()); + + node_type currentNode; + unsigned col, row, row2, openSetIndex; + node_type adjacentKey; + unsigned adjacentIndex; + weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight; + DataStructures::Map *adjacencyList; + DataStructures::Heap minHeap; + DataStructures::Map openSet; + + for (col=0; col < adjacencyLists.Size(); col++) + { + // This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck + costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true); + } + for (col=0; col < adjacencyLists.Size() * adjacencyLists.Size(); col++) + costMatrix[col]=INFINITE_WEIGHT; + currentNode=startNode; + row=0; + currentNodeWeight=0; + rootNode=startNode; + + // Clear the starting node column + if (adjacencyLists.Size()) + { + adjacentIndex=adjacencyLists.GetIndexAtKey(startNode); + for (row2=0; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=0; + } + + while (row < adjacencyLists.Size()-1) + { + adjacencyList = adjacencyLists.Get(currentNode); + // Go through all connections from the current node. If the new weight is less than the current weight, then update that weight. + for (col=0; col < adjacencyList->Size(); col++) + { + edgeWeight=(*adjacencyList)[col]; + adjacentKey=adjacencyList->GetKeyAtIndex(col); + adjacentIndex=adjacencyLists.GetIndexAtKey(adjacentKey); + adjacentNodeWeight=costMatrix[row*adjacencyLists.Size() + adjacentIndex]; + + if (currentNodeWeight + edgeWeight < adjacentNodeWeight) + { + // Update the weight for the adjacent node + for (row2=row; row2 < adjacencyLists.Size(); row2++) + costMatrix[row2*adjacencyLists.Size() + adjacentIndex]=currentNodeWeight + edgeWeight; + openSet.Set(adjacentKey, currentNodeWeight + edgeWeight); + } + } + + // Find the lowest in the open set + minHeap.Clear(); + for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++) + minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex)); + + /* + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } + */ + + if (minHeap.Size()==0) + { + // Unreachable nodes + isValidPath=true; + return; + } + + currentNodeWeight=minHeap.PeekWeight(0); + leastNodeArray[row]=minHeap.Pop(0); + currentNode=leastNodeArray[row]; + openSet.Delete(currentNode); + row++; + } + + /* +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size()-1; i++) + { + for (j=0; j < adjacencyLists.Size(); j++) + { + RAKNET_DEBUG_PRINTF("%2i ", costMatrix[i*adjacencyLists.Size() + j]); + } + RAKNET_DEBUG_PRINTF("Node=%i", leastNodeArray[i]); + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + */ + + isValidPath=true; + } + + template + void WeightedGraph::ClearDijkstra(void) + { + if (isValidPath) + { + isValidPath=false; + RakNet::OP_DELETE_ARRAY(costMatrix); + RakNet::OP_DELETE_ARRAY(leastNodeArray); + costMatrixIndices.Clear(); + } + } + + template + void WeightedGraph::Print(void) + { +#ifdef _DEBUG + unsigned i,j; + for (i=0; i < adjacencyLists.Size(); i++) + { + //RAKNET_DEBUG_PRINTF("%i connected to ", i); + RAKNET_DEBUG_PRINTF("%s connected to ", adjacencyLists.GetKeyAtIndex(i).systemAddress.ToString()); + + if (adjacencyLists[i]->Size()==0) + RAKNET_DEBUG_PRINTF(""); + else + { + for (j=0; j < adjacencyLists[i]->Size(); j++) + // RAKNET_DEBUG_PRINTF("%i (%.2f) ", adjacencyLists.GetIndexAtKey(adjacencyLists[i]->GetKeyAtIndex(j)), (float) adjacencyLists[i]->operator[](j) ); + RAKNET_DEBUG_PRINTF("%s (%.2f) ", adjacencyLists[i]->GetKeyAtIndex(j).systemAddress.ToString(), (float) adjacencyLists[i]->operator[](j) ); + } + + RAKNET_DEBUG_PRINTF("\n"); + } +#endif + } +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/Multiplayer/raknet/DataBlockEncryptor.h b/Multiplayer/raknet/DataBlockEncryptor.h new file mode 100644 index 000000000..700ab5931 --- /dev/null +++ b/Multiplayer/raknet/DataBlockEncryptor.h @@ -0,0 +1,72 @@ +/// \file +/// \brief \b [Internal] Encrypts and decrypts data blocks. Used as part of secure connections. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __DATA_BLOCK_ENCRYPTOR_H +#define __DATA_BLOCK_ENCRYPTOR_H + +#include "Rijndael.h" +#include "RakMemoryOverride.h" +class RakNetRandom; + +/// 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 6 data bytes and then pads the number of bytes to be a multiple of 16. Output should be large enough to hold this. + /// Output can be the same memory block as input + /// \param[in] input the input buffer to encrypt + /// \param[in] inputLength the size of the @em input buffer + /// \param[in] output the output buffer to store encrypted data + /// \param[in] outputLength the size of the output buffer + void Encrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength, RakNetRandom *rnr ); + + /// Decryption removes bytes, as few as 6. Output should be large enough to hold this. + /// Output can be the same memory block as input + /// \param[in] input the input buffer to decrypt + /// \param[in] inputLength the size of the @em input buffer + /// \param[in] output the output buffer to store decrypted data + /// \param[in] outputLength the size of the @em output buffer + /// \return False on bad checksum or input, true on success + bool Decrypt( unsigned char *input, unsigned int inputLength, unsigned char *output, unsigned int *outputLength ); + +protected: + + keyInstance keyEncrypt; + keyInstance keyDecrypt; + cipherInstance cipherInst; + bool keySet; +}; + +#endif diff --git a/Multiplayer/raknet/DataCompressor.h b/Multiplayer/raknet/DataCompressor.h new file mode 100644 index 000000000..f7987298e --- /dev/null +++ b/Multiplayer/raknet/DataCompressor.h @@ -0,0 +1,34 @@ +/// \file +/// \brief DataCompressor does compression on a block of data. Not very good compression, but it's small and fast so is something you can use per-message at runtime. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + + +#ifndef __DATA_COMPRESSOR_H +#define __DATA_COMPRESSOR_H + +#include "RakMemoryOverride.h" +#include "DS_HuffmanEncodingTree.h" +#include "Export.h" + +/// \brief Does compression on a block of data. Not very good compression, but it's small and fast so is something you can compute at runtime. +class RAK_DLL_EXPORT DataCompressor +{ +public: + static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output ); + static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output ); +}; + +#endif diff --git a/Multiplayer/raknet/DirectoryDeltaTransfer.h b/Multiplayer/raknet/DirectoryDeltaTransfer.h new file mode 100644 index 000000000..342907dac --- /dev/null +++ b/Multiplayer/raknet/DirectoryDeltaTransfer.h @@ -0,0 +1,143 @@ +/// \file +/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __DIRECTORY_DELTA_TRANSFER_H +#define __DIRECTORY_DELTA_TRANSFER_H + +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface.h" +#include "DS_Map.h" +#include "PacketPriority.h" + +class RakPeerInterface; +class FileList; +struct Packet; +struct InternalPacket; +struct DownloadRequest; +class FileListTransfer; +class FileListTransferCBInterface; +class FileListProgress; +class IncrementalReadInterface; + +/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer +/// \ingroup PLUGINS_GROUP + +/// \brief Simple class to send changes between directories. In essence, a simple autopatcher that can be used for transmitting levels, skins, etc. +/// \sa Autopatcher class for database driven patching, including binary deltas and search by date. +/// +/// To use, first set the path to your application. For example "C:/Games/MyRPG/" +/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative +/// to the path to your application. This includes subdirectories. +/// For example: +/// SetApplicationDirectory("C:/Games/MyRPG/"); +/// AddUploadsFromSubdirectory("Mods/Skins/"); +/// would allow downloads from +/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*" +/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows +/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems. +/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP +class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface +{ +public: + /// Constructor + DirectoryDeltaTransfer(); + + /// Destructor + virtual ~DirectoryDeltaTransfer(); + + /// This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files. + /// So you need an instance of that plugin registered with RakPeerInterface, and a pointer to that interface should be passed here. + /// \param[in] flt A pointer to a registered instance of FileListTransfer + void SetFileListTransferPlugin(FileListTransfer *flt); + + /// Set the local root directory to base all file uploads and downloads off of. + /// \param[in] pathToApplication This path will be prepended to \a applicationSubdirectory in AddUploadsFromSubdirectory to find the actual path on disk. + void SetApplicationDirectory(const char *pathToApplication); + + /// What parameters to use for the RakPeerInterface::Send() call when uploading files. + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + void SetUploadSendParameters(PacketPriority _priority, char _orderingChannel); + + /// Add all files in the specified subdirectory recursively + /// \a subdir is appended to \a pathToApplication in SetApplicationDirectory(). + /// All files in the resultant directory and subdirectories are then hashed so that users can download them. + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \param[in] subdir Concatenated with pathToApplication to form the final path from which to allow uploads. + void AddUploadsFromSubdirectory(const char *subdir); + + /// Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory. + /// \a subdir must contain all starting characters in \a subdir in AddUploadsFromSubdirectory + /// Therefore, + /// AddUploadsFromSubdirectory("Levels/Level1/"); would allow you to download using DownloadFromSubdirectory("Levels/Level1/Textures/"... + /// but it would NOT allow you to download from DownloadFromSubdirectory("Levels/"... or DownloadFromSubdirectory("Levels/Level2/"... + /// \pre You must call SetFileListTransferPlugin with a valid FileListTransfer plugin + /// \param[in] subdir A directory passed to AddUploadsFromSubdirectory on the remote system. The passed dir can be more specific than the remote dir. + /// \param[in] outputSubdir The directory to write the output to. Usually this will match \a subdir but it can be different if you want. + /// \param[in] prependAppDirToOutputSubdir True to prepend outputSubdir with pathToApplication when determining the final output path. Usually you want this to be true. + /// \param[in] host The address of the remote system to send the message to. + /// \param[in] onFileCallback Callback to call per-file (optional). When fileIndex+1==setCount in the callback then the download is done + /// \param[in] _priority See RakPeerInterface::Send() + /// \param[in] _orderingChannel See RakPeerInterface::Send() + /// \param[in] cb Callback to get progress updates. Pass 0 to not use. + /// \return A set ID, identifying this download set. Returns 65535 on host unreachable. + unsigned short DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb); + + /// Clear all allowed uploads previously set with AddUploadsFromSubdirectory + void ClearUploads(void); + + /// Returns how many files are available for upload + /// \return How many files are available for upload + unsigned GetNumberOfFilesForUpload(void) const; + + /// Set if we should compress outgoing sends or not + /// Defaults to false, because this results in a noticeable freeze on large requests + /// You can set this to true if you only send small files though + /// \param[in] compress True to compress, false to not. + void SetCompressOutgoingSends(bool compress); + + /// Normally, if a remote system requests files, those files are all loaded into memory and sent immediately. + /// This function allows the files to be read in incremental chunks, saving memory + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); + + /// \internal For plugin handling + virtual void OnAttach(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void Update(RakPeerInterface *peer); + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + /// \internal For plugin handling + virtual void OnShutdown(RakPeerInterface *peer); +protected: + void OnDownloadRequest(RakPeerInterface *peer, Packet *packet); + + char applicationDirectory[512]; + FileListTransfer *fileListTransfer; + FileList *availableUploads; + RakPeerInterface *rakPeer; + PacketPriority priority; + char orderingChannel; + bool compressOutgoingSends; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; +}; + +#endif diff --git a/Multiplayer/raknet/EmailSender.h b/Multiplayer/raknet/EmailSender.h new file mode 100644 index 000000000..b45e57def --- /dev/null +++ b/Multiplayer/raknet/EmailSender.h @@ -0,0 +1,51 @@ +/// \file +/// \brief Rudimentary class to send email from code. Don't expect anything fancy. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __EMAIL_SENDER_H +#define __EMAIL_SENDER_H + +class FileList; +class TCPInterface; +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" + +/// \brief Rudimentary class to send email from code. +class EmailSender +{ +public: + /// Sends an email + /// \param[in] hostAddress The address of the email server. + /// \param[in] hostPort The port of the email server (usually 25) + /// \param[in] sender The email address you are sending from. + /// \param[in] recipient The email address you are sending to. + /// \param[in] senderName The email address you claim to be sending from + /// \param[in] recipientName The email address you claim to be sending to + /// \param[in] subject Email subject + /// \param[in] body Email body + /// \param[in] attachedFiles List of files to attach to the email. (Can be 0 to send none). + /// \param[in] doPrintf true to output SMTP info to console(for debugging?) + /// \param[in] password Used if the server uses AUTHENTICATE PLAIN over TLS (such as gmail) + /// \return 0 on success, otherwise a string indicating the error message + const char *Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password); + + // Returns how many bytes were written + int Base64Encoding(const char *inputData, int dataLength, char *outputData, const char *base64Map); +protected: + const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf); +}; + +#endif diff --git a/Multiplayer/raknet/EpochTimeToString.h b/Multiplayer/raknet/EpochTimeToString.h new file mode 100644 index 000000000..6381b9608 --- /dev/null +++ b/Multiplayer/raknet/EpochTimeToString.h @@ -0,0 +1,9 @@ +#ifndef __EPOCH_TIME_TO_STRING_H +#define __EPOCH_TIME_TO_STRING_H + +#include "Export.h" + +RAK_DLL_EXPORT char * EpochTimeToString(long long time); + +#endif + diff --git a/Multiplayer/raknet/ExtendedOverlappedPool.h b/Multiplayer/raknet/ExtendedOverlappedPool.h new file mode 100644 index 000000000..efe8532ed --- /dev/null +++ b/Multiplayer/raknet/ExtendedOverlappedPool.h @@ -0,0 +1,50 @@ +/// \file +/// \brief \b [Depreciated] This was used for IO completion ports. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +// No longer used as I no longer support IO Completion ports +/* +#ifdef __USE_IO_COMPLETION_PORTS +#ifndef __EXTENDED_OVERLAPPED_POOL +#define __EXTENDED_OVERLAPPED_POOL +#include "SimpleMutex.h" +#include "ClientContextStruct.h" +#include "DS_Queue.h" + +/// Depreciated - for IO completion ports +class ExtendedOverlappedPool +{ + +public: + ExtendedOverlappedPool(); + ~ExtendedOverlappedPool(); + ExtendedOverlappedStruct* GetPointer( void ); + void ReleasePointer( ExtendedOverlappedStruct *p ); + static inline ExtendedOverlappedPool* Instance() + { + return & I; + } + +private: + DataStructures::Queue pool; + SimpleMutex poolMutex; + static ExtendedOverlappedPool I; +}; + +#endif +#endif + +*/ diff --git a/Multiplayer/raknet/FCMHost.h b/Multiplayer/raknet/FCMHost.h new file mode 100644 index 000000000..272c8723e --- /dev/null +++ b/Multiplayer/raknet/FCMHost.h @@ -0,0 +1,139 @@ +/// \file +/// \brief In a fully connected mesh, determines which system can act as a host. This generally is the system that has been connected to the mesh the longest. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __FCM_HOST_H +#define __FCM_HOST_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface.h" +#include "PacketPriority.h" +#include "DS_List.h" +#include "DS_Map.h" + +class RakPeerInterface; +struct Packet; + +typedef unsigned char FCMHostGroupID; + +/// \defgroup FCM_HOST_GROUP FullyConnectedMeshHost +/// \ingroup PLUGINS_GROUP + +/// \brief In a fully connected mesh of peers, allows all peers to agree that another peer is hot. +/// +/// In peer to peer games it is often still useful to have one system act as a host / server +/// For example, if the game has AI, only the host / server should control the AI +/// This plugin automatically determines the host, changing the host as the prior host disconnects, and allowing all peers to agree on who is the host +/// +/// Algorithm: +/// 1. Every system has a synchronized list of RakNetGuid, representing remote systems in the mesh +/// 2. The system at the head of the list is the host. +/// 3. New systems to join the mesh are added to the end of the list. +/// 4. To avoid contention, only the current host modifies or broadcasts the list. Modifications are broadcast to other systems. +/// 5. If the connection to the host is lost, the next connected system in the list becomes the host. +/// 6. If two meshes join each other, the host with the lower RakNetGuid takes precedence, and the lower priority mesh is appended. +/// +/// \pre A fully connected mesh is required among the participants - e.g. all participants must eventually connect to all other participants. Otherwise the algorithm will not return the same host among all systems. +/// \note ID_FCM_HOST_CHANGED is returned to RakPeerInterface::Receive() every time the host changes. +/// \ingroup FCM_HOST_GROUP +class RAK_DLL_EXPORT FCMHost : public PluginInterface +{ +public: + /// Constructor + FCMHost(); + + /// Destructor + virtual ~FCMHost(); + + /// Add a connected system to the list of systems to manage host tracking + /// \param[in] guid The system to add + /// \param[in] groupId Which group to assign this system to. Groups are used if you want to manage more than one list at a time. Use 0 if you don't care. + void AddParticipant(RakNetGUID guid, FCMHostGroupID groupId=0); + + /// Remove a participant added with AddParticipant(), or automatically added with SetAutoAddNewConnections(), from all groups + /// \param[in] guid The system to remove + void RemoveParticipantFromAllGroups(RakNetGUID guid); + + /// Remove a participant added with AddParticipant(), or automatically added with SetAutoAddNewConnections() + /// \param[in] guid The system to remove + /// \param[in] groupId Which group to remove this system from. Groups are used if you want to manage more than one list at a time. Use 0 if you don't care. + void RemoveParticipant(RakNetGUID guid, FCMHostGroupID groupId=0); + + /// If set to true, all subsequent new connections will be added to \a groupId. + /// \param[in] autoAdd If true, removes the need to call AddParticipant() on ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED + /// Which group to automatically add systems to. Unused parameter if \a autoAdd is false + void SetAutoAddNewConnections(bool autoAdd, FCMHostGroupID groupId=0); + + /// Get the current host. This will usually be the oldest system added with AddParticipant() or automatically added with SetAutoAddNewConnections() + /// \param[in] groupId Which group we are referring to. + /// \return A RakNetGuid representing which system should be the host. We are NOT necessarily connected to this system, if the mesh is not fully connected (yet?) + RakNetGUID GetHost(FCMHostGroupID groupId=0) const; + + /// Convenience function to return if the attached instance of RakPeerInterface is the host + /// \param[in] groupId Which group we are referring to. + /// \return true if this system is the host, false otherwise. + bool AmIHost(FCMHostGroupID groupId=0) const; + + /// Returns if we are connected to other systems, and one of those systems (possibly ourselves) is a host + /// If we are not connected to other systems, then our own system is always the host, and in a sense the host is undetermined + /// This function could also be named HasValidHost, or IsHostDetermined + /// \param[in] groupId Which group we are referring to. + /// \return true If there is a networked host + bool HasConnectedHost(FCMHostGroupID groupId=0) const; + + /// Convenience function to return if the attached instance of RakPeerInterface is the host + /// Differs from AmIHost in that if this system is the only system in the mesh, returns false + /// \param[in] groupId Which group we are referring to. + /// \return true if this system is the host, false otherwise. + bool AmIConnectedHost(FCMHostGroupID groupId=0) const; + + /// \internal For plugin handling + virtual void OnAttach(RakPeerInterface *peer); + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + /// \internal For plugin handling + virtual void OnShutdown(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); +protected: + + void Clear(void); + + void OnAddParticipantRequest(RakPeerInterface *peer, Packet *packet); + void OnNewConnection(RakPeerInterface *peer, Packet *packet); + void OnHostListUpdate(RakPeerInterface *peer, Packet *packet); + void WriteHostListGroup(FCMHostGroupID groupId, RakNet::BitStream *bs); + void ReadHostListGroup(RakNet::BitStream *bs, DataStructures::List *theList); + void BroadcastToGroup(FCMHostGroupID groupId, RakNet::BitStream *bs); + void BroadcastGroupList(FCMHostGroupID groupId); + void NotifyUserOfHost(FCMHostGroupID groupId, RakNetGUID guid); + void RemoveParticipantFromGroup(RakNetGUID guid, FCMHostGroupID groupId); + + /// List of hosts, in a map of groupIDs + DataStructures::Map* > hostListsGroup; + + // All systems added with AddParticipant(). If we become the host, and a system is in this list and not in hostListsGroup, + // Then we add it. + DataStructures::Map* > participantList; + + RakPeerInterface *rakPeer; + + bool autoAddConnections; + FCMHostGroupID autoAddConnectionsTargetGroup; +}; + +#endif diff --git a/Multiplayer/raknet/FileList.h b/Multiplayer/raknet/FileList.h new file mode 100644 index 000000000..02d481e43 --- /dev/null +++ b/Multiplayer/raknet/FileList.h @@ -0,0 +1,197 @@ +#ifndef __FILE_LIST +#define __FILE_LIST + +#include "Export.h" +#include "DS_List.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "FileListNodeContext.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +namespace RakNet +{ + class BitStream; +} + +/// Represents once instance of a file +struct FileListNode +{ + /// Name of the file + char *filename; + + /// File data (may be null if not ready) + char *data; + + /// Length of \a data. May be greater than fileLength if prepended with a file hash + BitSize_t dataLengthBytes; + + /// Length of the file + unsigned fileLengthBytes; + + /// User specific data for whatever, describing this file. + FileListNodeContext context; + + /// If true, data and dataLengthBytes should be empty. This is just storing the filename + bool isAReference; +}; + +//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data ); + +class RakPeerInterface; +class FileList; + +/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called +class RAK_DLL_EXPORT FileListProgress +{ +public: + FileListProgress() {} + virtual ~FileListProgress() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) { + (void) fileList; + (void) dir; + } + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) { + (void) fileList; + (void) dir; + (void) directoriesRemaining; + } + + /// Called for each file, when that file begins processing + virtual void OnFile(FileList *fileList, char *dir, char *fileName, unsigned int fileSize) { + (void) fileList; + (void) dir; + (void) fileName; + (void) fileSize; + } + + /// This function is called when we are sending a file to a remote system + /// \param[in] fileName The name of the file being sent + /// \param[in] fileLengthBytes How long the file is + /// \param[in] offset The offset in bytes into the file that we are sending + /// \param[in] bytesBeingSent How many bytes we are sending this push + /// \param[in] done If this file is now done with this push + /// \param[in] targetSystem Who we are sending to + virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem) + { + (void) fileName; + (void) fileLengthBytes; + (void) offset; + (void) bytesBeingSent; + (void) done; + (void) targetSystem; + } +}; + +/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF +class RAK_DLL_EXPORT FLP_Printf : public FileListProgress +{ +public: + FLP_Printf() {} + virtual ~FLP_Printf() {} + + /// First callback called when FileList::AddFilesFromDirectory() starts + virtual void OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir); + + /// Called for each directory, when that directory begins processing + virtual void OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining); +}; + +class RAK_DLL_EXPORT FileList +{ +public: + FileList(); + ~FileList(); + /// Add all the files at a given directory. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[in] subDirectory The rest of the path to the file. This is stored as a prefix to the filename + /// \param[in] writeHash The first SHA1_LENGTH bytes is a hash of the file, with the remainder the actual file data (should \a writeData be true) + /// \param[in] writeData Write the contents of each file + /// \param[in] recursive Whether or not to visit subdirectories + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context); + + /// Deallocate all memory + void Clear(void); + + /// Write all encoded data into a bitstream + void Serialize(RakNet::BitStream *outBitStream); + + /// Read all encoded data from a bitstream. Clear() is called before deserializing. + bool Deserialize(RakNet::BitStream *inBitStream); + + /// Given the existing set of files, search applicationDirectory for the same files. + /// For each file that is missing or different, add that file to \a missingOrChangedFiles. Note: the file contents are not written, and only the hash if written if \a alwaysWriteHash is true + /// alwaysWriteHash and neverWriteHash are optimizations to avoid reading the file contents to generate the hash if not necessary because the file is missing or has different lengths anyway. + /// \param[in] applicationDirectory The first part of the path. This is not stored as part of the filename. Use \ as the path delineator. + /// \param[out] missingOrChangedFiles Output list written to + /// \param[in] alwaysWriteHash If true, and neverWriteHash is false, will hash the file content of the file on disk, and write that as the file data with a length of SHA1_LENGTH bytes. If false, if the file length is different, will only write the filename. + /// \param[in] neverWriteHash If true, will never write the hash, even if available. If false, will write the hash if the file lengths are the same and it was forced to do a comparison. + void ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash); + + /// Return the files that need to be written to make \a input match this current FileList. + /// Specify dirSubset to only consider files that start with this path + /// specify remoteSubdir to assume that all filenames in input start with this path, so strip it off when comparing filenames. + /// \param[in] input Full list of files + /// \param[out] output Files that we need to match input + /// \param[in] dirSubset If the filename does not start with this path, just skip this file. + /// \param[in] remoteSubdir Remove this from the filenames of \a input when comparing to existing filenames. + void GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir); + + /// Assuming FileList contains a list of filenames presumably without data, read the data for these filenames + /// \param[in] applicationDirectory Prepend this path to each filename. Trailing slash will be added if necessary. Use \ as the path delineator. + /// \param[in] writeFileData True to read and store the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] writeFileHash True to read and store the hash of the file data. The first SHA1_LENGTH bytes will contain the hash if \a writeFileHash is true + /// \param[in] removeUnknownFiles If a file does not exist on disk but is in the file list, remove it from the file list? + void PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles); + + /// By default, GetDeltaToCurrent tags files as non-references, meaning they are assumed to be populated later + /// This tags all files as references, required for IncrementalReadInterface to process them incrementally + void FlagFilesAsReferences(void); + + /// Write all files to disk, prefixing the paths with applicationDirectory + /// \param[in] applicationDirectory path prefix + void WriteDataToDisk(const char *applicationDirectory); + + /// Add a file, given data already in memory + /// \param[in] filename Name of a file, optionally prefixed with a partial or complete path. Use \ as the path delineator. + /// \param[in] data Contents to write + /// \param[in] dataLength length of the data, which may be greater than fileLength should you prefix extra data, such as the hash + /// \param[in] fileLength Length of the file + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + /// \param[in] isAReference Means that this is just a reference to a file elsewhere - does not actually have any data + void AddFile(const char *filename, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false); + + /// Add a file, reading it from disk + /// \param[in] filepath Complete path to the file, including the filename itself + /// \param[in] filename filename to store internally, anything you want, but usually either the complete path or a subset of the complete path. + /// \param[in] context User defined byte to store with each file. Use for whatever you want. + void AddFile(const char *filepath, const char *filename, FileListNodeContext context); + + /// Delete all files stored in the file list + /// \param[in] applicationDirectory Prefixed to the path to each filename. Use \ as the path delineator. + void DeleteFiles(const char *applicationDirectory); + + /// Set a callback to get progress reports about what this class does + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void SetCallback(FileListProgress *cb); + + // Here so you can read it, but don't modify it + DataStructures::List fileList; + + static bool FixEndingSlash(char *str); +protected: + FileListProgress *callback; +}; + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/Multiplayer/raknet/FileListNodeContext.h b/Multiplayer/raknet/FileListNodeContext.h new file mode 100644 index 000000000..a0f805e86 --- /dev/null +++ b/Multiplayer/raknet/FileListNodeContext.h @@ -0,0 +1,14 @@ +#ifndef __FILE_LIST_NODE_CONTEXT_H +#define __FILE_LIST_NODE_CONTEXT_H + +struct FileListNodeContext +{ + FileListNodeContext() {} + FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {} + ~FileListNodeContext() {} + + unsigned char op; + unsigned int fileId; +}; + +#endif diff --git a/Multiplayer/raknet/FileListTransfer.h b/Multiplayer/raknet/FileListTransfer.h new file mode 100644 index 000000000..0ca453645 --- /dev/null +++ b/Multiplayer/raknet/FileListTransfer.h @@ -0,0 +1,130 @@ +/// \file +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __FILE_LIST_TRANFER_H +#define __FILE_LIST_TRANFER_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "PluginInterface.h" +#include "DS_Map.h" +#include "RakNetTypes.h" +#include "PacketPriority.h" +#include "RakMemoryOverride.h" +#include "FileList.h" + +class IncrementalReadInterface; +class FileListTransferCBInterface; +class FileListProgress; +struct FileListReceiver; + +/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer +/// \ingroup PLUGINS_GROUP + +/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure. +/// Similar to the DirectoryDeltaTransfer plugin, except that it doesn't send deltas based on pre-existing files or actually write the files to disk. +/// +/// Usage: +/// Call SetupReceive to allow one file set to arrive. The value returned by FileListTransfer::SetupReceive() +/// is the setID that is allowed. +/// It's up to you to transmit this value to the other system, along with information indicating what kind of files you want to get. +/// The other system should then prepare a FileList and call FileListTransfer::Send(), passing the return value of FileListTransfer::SetupReceive() +/// as the \a setID parameter to FileListTransfer::Send() +/// \ingroup FILE_LIST_TRANSFER_GROUP +class RAK_DLL_EXPORT FileListTransfer : public PluginInterface +{ +public: + FileListTransfer(); + virtual ~FileListTransfer(); + + /// Allows one corresponding Send() call from another system to arrive. + /// \param[in] handler The class to call on each file + /// \param[in] deleteHandler True to delete the handler when it is no longer needed. False to not do so. + /// \param[in] allowedSender Which system to allow files from. + /// \return A set ID value, which should be passed as the \a setID value to the Send() call on the other system. This value will be returned in the callback and is unique per file set. Returns 65535 on failure (not connected to sender) + unsigned short SetupReceive(FileListTransferCBInterface *handler, bool deleteHandler, SystemAddress allowedSender); + + /// Send the FileList structure to another system, which must have previously called SetupReceive() + /// \param[in] fileList A list of files. The data contained in FileList::data will be sent incrementally and compressed among all files in the set + /// \param[in] rakPeer The instance of RakNet to use to send the message + /// \param[in] recipient The address of the system to send to + /// \param[in] setID The return value of SetupReceive() which was previously called on \a recipient + /// \param[in] priority Passed to RakPeerInterface::Send() + /// \param[in] orderingChannel Passed to RakPeerInterface::Send() + /// \param[in] compressData Depreciated, unsupported + /// \param[in] _incrementalReadInterface If a file in \a fileList has no data, filePullInterface will be used to read the file in chunks of size \a chunkSize + /// \param[in] _chunkSize How large of a block of a file to send at once + void Send(FileList *fileList, RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, bool compressData, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=8388608); + + /// Stop a download. + void CancelReceive(unsigned short setId); + + /// Remove all handlers associated with a particular system address + void RemoveReceiver(SystemAddress systemAddress); + + /// Is a handler passed to SetupReceive still running? + bool IsHandlerActive(unsigned short setId); + + /// Set a callback to get progress reports about what the file list instances do + /// \param[in] cb A pointer to an externally defined instance of FileListProgress. This pointer is held internally, so should remain valid as long as this class is valid. + void SetCallback(FileListProgress *cb); + + /// \returns what was sent to SetCallback + /// \return What was sent to SetCallback + FileListProgress *GetCallback(void) const; + + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + /// \internal For plugin handling + virtual void OnShutdown(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + /// \internal For plugin handling + virtual void OnAttach(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void Update(RakPeerInterface *peer); + +protected: + bool DecodeSetHeader(Packet *packet); + bool DecodeFile(Packet *packet, bool fullFile); + + void Clear(void); + + void OnReferencePush(Packet *packet, bool fullFile); + void StoreForPush(FileListNodeContext context, unsigned short _setID, const char *fileName, unsigned int _setIndex, unsigned fileLengthBytes, unsigned dataLengthBytes, SystemAddress recipient, PacketPriority packetPriority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize); + + DataStructures::Map fileListReceivers; + unsigned short setId; + RakPeerInterface *rakPeer; + FileListProgress *callback; + + struct FileToPush + { + FileListNode fileListNode; + SystemAddress systemAddress; + PacketPriority packetPriority; + char orderingChannel; + unsigned int currentOffset; + unsigned short setID; + unsigned int setIndex; + IncrementalReadInterface *incrementalReadInterface; + unsigned int chunkSize; + }; + DataStructures::List filesToPush; +}; + +#endif diff --git a/Multiplayer/raknet/FileListTransferCBInterface.h b/Multiplayer/raknet/FileListTransferCBInterface.h new file mode 100644 index 000000000..eabb8d9a8 --- /dev/null +++ b/Multiplayer/raknet/FileListTransferCBInterface.h @@ -0,0 +1,99 @@ +#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H +#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H + +#include "RakMemoryOverride.h" +#include "FileListNodeContext.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +/// \brief Used by FileListTransfer plugin as a callback for when we get a file. +/// You get the last file when fileIndex==setCount +/// \sa FileListTransfer +class FileListTransferCBInterface +{ +public: + struct OnFileStruct + { + /// The index into the set of files, from 0 to setCount + unsigned fileIndex; + + /// The name of the file + char fileName[512]; + + /// The data pointed to by the file + char *fileData; + + /// How many bytes this file took to send, if using compression. + unsigned compressedTransmissionLength; + + /// The actual length of this file. + BitSize_t finalDataLength; + + /// Files are transmitted in sets, where more than one set of files can be transmitted at the same time. + /// This is the identifier for the set, which is returned by FileListTransfer::SetupReceive + unsigned short setID; + + /// The number of files that are in this set. + unsigned setCount; + + /// The total length of the transmitted files for this set, with compression. + unsigned setTotalCompressedTransmissionLength; + + /// The total length of the transmitted files for this set, after being uncompressed + unsigned setTotalFinalLength; + + /// User data passed to one of the functions in the FileList class. + /// However, on error, this is instead changed to one of the enumerations in the PatchContext structure. + FileListNodeContext context; + }; + + FileListTransferCBInterface() {} + virtual ~FileListTransferCBInterface() {} + + /// Got a file + /// This structure is only valid for the duration of this function call. + /// \return Return true to have RakNet delete the memory allocated to hold this file for this function call. + virtual bool OnFile(OnFileStruct *onFileStruct)=0; + + /// Got part of a big file. + /// You can get these notifications by calling RakPeer::SetSplitMessageProgressInterval + /// Otherwise you will only get complete files. + /// \param[in] onFileStruct General information about this file, such as the filename and the first \a partLength bytes. You do NOT need to save this data yourself. The complete file will arrive normally. + /// \param[in] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal + /// \param[in] partTotal The total number of parts this file was split into. Each part will be roughly the MTU size, minus the UDP header and RakNet headers + /// \param[in] partLength How many bytes long firstDataChunk is + /// \param[in] firstDataChunk The first \a partLength of the final file. If you store identifying information about the file in the first \a partLength bytes, you can read them while the download is taking place. If this hasn't arrived yet, firstDataChunk will be 0 + virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk) { + (void) onFileStruct; + (void) partCount; + (void) partTotal; + (void) partLength; + (void) firstDataChunk; + } + + /// Called while the handler is active by FileListTransfer + /// Return false when you are done with the class + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + virtual bool Update(void) {return true;} + + /// Called when the download is completed. + /// If you are finished with this class, return false + /// At that point OnDereference will be called and the class will no longer be maintained by the FileListTransfer plugin. + /// Otherwise return true, and Update will continue to be called. + virtual bool OnDownloadComplete(void) {return false;} + + /// This function is called when this instance is about to be dereferenced by the FileListTransfer plugin. + /// Update will no longer be called. + /// It will will be deleted automatically if true was passed to FileListTransfer::SetupReceive::deleteHandler + /// Otherwise it is up to you to delete it yourself. + virtual void OnDereference(void) {} +}; + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/Multiplayer/raknet/FileOperations.h b/Multiplayer/raknet/FileOperations.h new file mode 100644 index 000000000..30bd426ab --- /dev/null +++ b/Multiplayer/raknet/FileOperations.h @@ -0,0 +1,13 @@ +#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); +unsigned int RAK_DLL_EXPORT GetFileLength(const char *path); + +#endif diff --git a/Multiplayer/raknet/FormatString.h b/Multiplayer/raknet/FormatString.h new file mode 100644 index 000000000..e80f38c22 --- /dev/null +++ b/Multiplayer/raknet/FormatString.h @@ -0,0 +1,12 @@ +#ifndef __FORMAT_STRING_H +#define __FORMAT_STRING_H + +#include "Export.h" + +extern "C" { +char * FormatString(const char *format, ...); +} + + +#endif + diff --git a/Multiplayer/raknet/FullyConnectedMesh.h b/Multiplayer/raknet/FullyConnectedMesh.h new file mode 100644 index 000000000..df350d5f4 --- /dev/null +++ b/Multiplayer/raknet/FullyConnectedMesh.h @@ -0,0 +1,65 @@ +/// \file +/// \brief Fully connected mesh plugin. This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __FULLY_CONNECTED_MESH_H +#define __FULLY_CONNECTED_MESH_H + +class RakPeerInterface; +class NatPunchthrough; +#include "PluginInterface.h" +#include "RakMemoryOverride.h" + +/// \defgroup FULLY_CONNECTED_MESH_GROUP FullyConnectedMesh +/// \ingroup PLUGINS_GROUP + +/// Fully connected mesh plugin. This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about. +/// \pre You must also install the ConnectionGraph plugin. If you want a password, set it there. +/// \ingroup FULLY_CONNECTED_MESH_GROUP +class FullyConnectedMesh : public PluginInterface +{ +public: + FullyConnectedMesh(); + virtual ~FullyConnectedMesh(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// Set the password to use to connect to the other systems + void Startup(const char *password, int _passwordLength); + + /// Use the NAT punchthrough system to connect rather than calling directly + /// \param[in] np Pointer to an attached instance of the NatPunchthrough plugin + /// \param[in] _facilitator Address of the NAT punchthrough facilitator + void ConnectWithNatPunchthrough(NatPunchthrough *np, SystemAddress _facilitator); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void OnShutdown(RakPeerInterface *peer); + virtual void Update(RakPeerInterface *peer); + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + + +protected: + char *pw; + int passwordLength; + + NatPunchthrough *natPunchthrough; + SystemAddress facilitator; +}; + +#endif diff --git a/Multiplayer/raknet/FunctionThread.h b/Multiplayer/raknet/FunctionThread.h new file mode 100644 index 000000000..8444a33c6 --- /dev/null +++ b/Multiplayer/raknet/FunctionThread.h @@ -0,0 +1,130 @@ +/// \file +/// \brief A set of classes to make it easier to perform asynchronous function processing. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __FUNCTION_THREAD_H +#define __FUNCTION_THREAD_H + +#include "SingleProducerConsumer.h" +#include "ThreadPool.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +namespace RakNet +{ + +// Forward declarations +class Functor; + +/// FunctionThread takes a stream of classes that implement a processing function, processes them in a thread, and calls a callback with the result. +/// It's a useful way to call blocking functions that you do not want to block, such as file writes and database operations. +class RAK_DLL_EXPORT FunctionThread +{ +public: + FunctionThread(); + ~FunctionThread(); + + struct FunctorAndContext + { + Functor *functor; + void *context; + }; + + /// Starts the thread up. + void StartThreads(int numThreads); + + /// Stop processing. Will also call FunctorResultHandler callbacks with /a wasCancelled set to true. + /// \param[in] blockOnCurrentProcessing Wait for the current processing to finish? + void StopThreads(bool blockOnCurrentProcessing); + + /// Add a functor to the incoming stream of functors + /// \note functor MUST be a valid pointer until Functor::HandleResult() is called, at which point the pointer is returned to you. + /// \note For practical purposes this means the instance of functor you pass to this function has to be allocated using new and delete. + /// \note You should deallocate the pointer inside Functor::HandleResult() + /// \param[in] functor A pointer to an implemented Functor class + /// \param[in] If there is some context to this functor you want to look up to cancel it, you can set it here. Returned back to you in Functor::HandleResult + void Push(Functor *functor, void *context=0); + + /// Call FunctorResultHandler callbacks + /// Normally you would call this once per update cycle, although you do not have to. + void CallResultHandlers(void); + + /// If you want to cancel input and output functors associated with some context, you can pass a function to do that here + /// \param[in] cancelThisFunctor Function should return true to cancel the functor, false to let it process + /// \param[in] userData Pointer to whatever you want. Passed to the cancelThisFunctor call + void CancelFunctorsWithContext(bool (*cancelThisFunctor)(FunctorAndContext func, void *userData), void *userData); + + /// If you want to automatically do some kind of processing on every functor after Functor::HandleResult is called, set it here. + /// Useful to cleanup FunctionThread::Push::context + /// \param[in] postResult pointer to a C function to do post-processing + void SetPostResultFunction(void (*postResult)(FunctorAndContext func)); + + +protected: + void CancelInput(void); + ThreadPool threadPool; + + void (*pr)(FunctorAndContext func); +}; + +/// A functor is a single unit of processing to send to the Function thread. +/// Derive from it, add your data, and implement the processing function. +class Functor +{ +public: + Functor() {} + virtual ~Functor() {} + + /// Do whatever processing you want. + /// \param[in] context pointer passed to FunctionThread::Push::context + virtual void Process(void *context)=0; + /// Called from FunctionThread::CallResultHandlers with wasCancelled false OR + /// Called from FunctionThread::StopThread or FunctionThread::~FunctionThread with wasCancelled true + /// \param[in] wasCancelledTrue if CallResultHandlers was called, false if StopThreads or CancelInputWithContext was called before Functor::Process() + /// \param[in] context pointer passed to FunctionThread::Push::context + virtual void HandleResult(bool wasCancelled, void *context)=0; +}; + +class RAK_DLL_EXPORT FunctionThreadDependentClass +{ +public: + FunctionThreadDependentClass(); + virtual ~FunctionThreadDependentClass(); + + /// Assigns a function thread to process asynchronous calls. If you do not assign one then one will be created automatically. + /// \param[in] ft An instance of a running function thread class. This class can be shared and used for other functors as well. + virtual void AssignFunctionThread(FunctionThread *ft); + + /// \return Returns the function thread held in the class + FunctionThread *GetFunctionThread(void) const; + + /// \returns Whether or not this class allocated the function thread by itself + bool GetFunctionThreadWasAllocated(void) const; + + /// Allocates and starts the thread if needed, and pushes the functor + /// \param[in] functor Functor to push + /// \param[in] context Sent to FunctionThread::Push::context + virtual void PushFunctor(Functor *functor, void *context=0); +protected: + /// Allocates and starts the function thread, if necessary + void StartFunctionThread(); + FunctionThread *functionThread; + bool functionThreadWasAllocated; +}; + +} + +#endif diff --git a/Multiplayer/raknet/Gen_RPC8.h b/Multiplayer/raknet/Gen_RPC8.h new file mode 100644 index 000000000..1f6f98bcb --- /dev/null +++ b/Multiplayer/raknet/Gen_RPC8.h @@ -0,0 +1,767 @@ +#ifndef __GEN_RPC8_H +#define __GEN_RPC8_H + +#include +#include +#include // memcpy +#include +#if defined(_XBOX) || defined(X360) +#include +#elif defined (_WIN32) +#include +#endif +#include +//#define ASSEMBLY_BLOCK asm +//#include "Types.h" +#include "BitStream.h" +// #define AUTO_RPC_NO_ASM + +#ifdef _WIN64 +#define AUTO_RPC_NO_ASM +#endif + +namespace GenRPC +{ + +//#define __BITSTREAM_NATIVE_END 1 + + +// -8<----8<----8<----BEGIN + +// +// 0. References +// a. Calling conventions for different C++ compilers and operating systems [http://www.agner.org/optimize] +// b. System V Application Binary Interface AMD64 Architecture Processor Supplement +// Used by 64-bit MAC and 64-bit Linux. +// c. 32-bit PowerPC MAC calling conventions [http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/32bitPowerPC.html#//apple_ref/doc/uid/TP40002438-SW20] +// d. 32-bit IA MAC calling conventions [http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/IA32.html#//apple_ref/doc/uid/TP40002492-SW4] +// e. Calling conventions on 64-bit windows [http://msdn2.microsoft.com/en-us/library/zthk2dkh(VS.80).aspx] +// f. 64-bit PowerPC MAC calling conventions [http://developer.apple.com/documentation/DeveloperTools/Conceptual/LowLevelABI/Articles/64bitPowerPC.html#//apple_ref/doc/uid/TP40002471] +// +// 1. General Introduction. +// +// Quite a lot of code we write hinges on hidden assumptions. For instance, most code tacitly +// assumes a char is 8 bits, even though it needn't be. And how much code relies on +// two's-a-compliment numbers, by, for example, assuming the only representation of zero +// is all bits clear? Yet this too isn't mandated by the C standard - which allows for non +// two's a compliment numbers. +// And the switch to 64-bit will see us discovering how +// much of our code assumes sizeof(int) == sizeof(long) == sizeof(void*) +// +// These tradeoffs and compromises are made because we know the architectures +// with CHAR_BITS != 8 are embedded systems of FPGAs and we don't extend our definition of +// portability to those systems. Or Windows code can +// assume its running on a little-endian machine, without loss of generality. In fact, it +// often impossible to test our code in situatiosn where assumptions are not true. +// +// The same is true of a lightweight RPC - to be possible at all, it will have make some +// assumptions about the architecture on (like CHAR_BITS == 8) which limit portability, but +// which are not unreasonable for the cases where its expected to run (modern personal computers) +// and hopefully can be extended easily to meet new cases. +// +// 2. Parameter passing - Introduction +// +// The C standard doesn't mandate how parameters are passed from one function to another. +// That's down to the particular archictecture, normally laid out in the Application Binary +// Interface (ABI). +// +// On some architecture (e.g. 32bit X86) the parameters are all passed on the stack; +// on some architectures (e.g. SPARC) there is no stack and they are all passed in register. +// Sometimes the function must be passed the exact number of args it expects (e.g. WIN32 +// "stdcall"); somtimes it can take an arbitrary number of args (IA-32/64 linux). +// +// But whatever the case, the compiler knows about all the details - it sorts them out every +// time we write a call. So to make it portable we must ensure we pass the compiler *sufficient* +// information to be able to encode the call, in all the cases we're interested in. To do this +// we need some knowledge of the ABI, without getting our hands dirty writing assembler. +// Not only because we can't all be experts at every particularl architecture with its necessary +// prefetches, conditional moves and innate parralelism, but also because that allows the compiler to +// code for the exact processors - rather than using a lowest-common denominator. +// +// +// 3. Preparing parameters and assumptions +// +// We assume that the processor has a 32 bit or 64 bit "natural word size" - and that the +// registers, stack entries (if a stack exists) and pointers all have this natural word +// size. We further assume (1) parameters smaller than this have to be padded out to meet this +// size, and that (2) this can be done by zero-extended them, regardless of whether they are signed +// or unsigned quanitites. +// +// The SysV ABI for 64bit X86 [b] and that ABI for 64-bit windows [e] require that floating point +// parameters are passed in floating points registers - so to work with these types we need to know +// they are floats and alert the compiler. A similar arrangement is true for both 32-bit and 64-bit +// Power PC systems. +// +// This can extend to structures ("aggregate types") containing floating point numbers - where +// individual members can still be passed in register. (Although +// on 64-bit windows, aggregates of size > 8 bytes are passed on the stack, so, +// except for the pathological case of struct S { double F }; there are no problems with +// structures containing floats.) +// +// --- + +// +// AUTO_RPC_MAX_PARAMS: Absolute number of stack words we can handle +// ABI: used to select features specific to ABI. +// AUTO_RPC_INT_REG_PARAMS: Number of parameters passed in integer registers. (Only used by SYSV ABI) +// AUTO_RPC_FLOAT_REG_PARAMS: Number of parameters passed in floating point registers. +// AUTO_RPC_INT_SHADOW_OF_FLOATS: Create a copy of the floats in the integer/stack space. +// AUTO_RPC_ALLOC_SEPARATE_FLOATS: Push floats to a separate contiguous floating point "stack". +// Ortherwise we rely on shadow. +// +// PARAMETER_REF_THRESHOLD: parameters bigger than this are replaced by a reference ( WIN64 ) +// +// +#define AUTO_RPC_MAX_PARAMS 64 + +#define AUTO_RPC_ABI_NONE 0 // not supported - should fail. + +#define AUTO_RPC_ABI_IA_32 1 // all parameters are passed on the stack. +// preserves: ebx,esi,edi,ebp + +#define AUTO_RPC_ABI_WIN_AMD64 2 // first four parameters in either +// rcx|xmm0, rdx|xmm1,r8|xmm2,r9|xmm3 +// preserves: rbx,rsi,rdi,rbp,r12-r15; xmm6-xmm15 +// comments: aggregates > 8 passed by ref; reg params shadowed on stack + +#define AUTO_RPC_ABI_SYSV_AMD64 3 // first six ints in: rdi,rsi,rdx,rcx,r8,r9 +// first eight reals: in xmm0...xmm7 +// preserves: rbx,rbp,r12-r15 +// comments: aggregates > 16 bumped to stack + +#define AUTO_RPC_ABI_PPC 4 // first 6 args (even if float) in int reg; first 13 floats in reg. +// parameter passing area with shadow area. + + +// Configure the parameters for the system. +#if defined(__i386__) || defined( _M_IX86 ) || defined( __INTEL__ ) +// 32 bit system. +#define AUTO_RPC_AUTORPC_WORD 32 + +typedef unsigned int NaturalWord; +typedef double HardwareReal; + +#define AUTO_RPC_INT_REG_PARAMS 0 +#define AUTO_RPC_FLOAT_REG_PARAMS 0 + +#define AUTO_RPC_ABI AUTO_RPC_ABI_IA_32 +#define AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD 0 +#define AUTO_RPC_INT_SHADOW_OF_FLOATS 1 +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 0 +#define AUTO_RPC_CREATE_FLOAT_MAP 0 + +#elif defined( _M_X64 ) || defined( __x86_64__ ) || defined( _M_AMD64 ) +#define AUTO_RPC_AUTORPC_WORD 64 + +#if defined( _WIN64 ) +#define AUTO_RPC_FLOAT_REG_PARAMS 4 +#define AUTO_RPC_INT_REG_PARAMS 4 +#define AUTO_RPC_ABI AUTO_RPC_ABI_WIN_AMD64 +#define AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD 8 +#define AUTO_RPC_INT_SHADOW_OF_FLOATS 1 +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 0 +#define AUTO_RPC_CREATE_FLOAT_MAP 1 +#else +#define AUTO_RPC_ABI AUTO_RPC_ABI_SYSV_AMD64 +#define AUTO_RPC_INT_REG_PARAMS 6 +#define AUTO_RPC_FLOAT_REG_PARAMS 8 +#define AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD 0 +#define AUTO_RPC_INT_SHADOW_OF_FLOATS 0 +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 1 +#define AUTO_RPC_CREATE_FLOAT_MAP 0 +#endif + +// NB OS's differ over. +typedef unsigned long long NaturalWord; +typedef double HardwareReal; // could be changed to __float128 on AMD64/nonwin + +#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +typedef double HardwareReal; +typedef unsigned long long NaturalWord; +#define AUTO_RPC_AUTORPC_WORD 64 +#define AUTO_RPC_INT_REG_PARAMS 8 +#define AUTO_RPC_FLOAT_REG_PARAMS 13 +#define AUTO_RPC_INT_SHADOW_OF_FLOATS 1 +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 1 +#define AUTO_RPC_CREATE_FLOAT_MAP 0 +#define AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD 0 +#define AUTO_RPC_ABI AUTO_RPC_ABI_PPC + +#elif defined(_M_PPC) || defined( __POWERPC__ ) + +#include + +/// PPC Mac doesn't support sizeof( long ) in an #if statement +#if defined (LONG_BIT) + #if LONG_BIT == 64 + #define AUTORPC_WORD 64 + typedef double HardwareReal; + typedef unsigned long long NaturalWord; + #else + #define AUTORPC_WORD 32 + typedef double HardwareReal; + typedef unsigned int NaturalWord; + #endif +#else + #if defined(_XBOX) || defined(X360) + #define AUTORPC_WORD 32 + typedef double HardwareReal; + typedef unsigned int NaturalWord; + #else + #if sizeof( long ) == 8 + #define AUTORPC_WORD 64 + typedef double HardwareReal; + typedef unsigned long long NaturalWord; + #else + #define AUTORPC_WORD 32 + typedef double HardwareReal; + typedef unsigned int NaturalWord; + #endif + #endif +#endif + +#define AUTO_RPC_INT_REG_PARAMS 8 +#define AUTO_RPC_FLOAT_REG_PARAMS 13 +#define AUTO_RPC_INT_SHADOW_OF_FLOATS 1 +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 1 +#define AUTO_RPC_CREATE_FLOAT_MAP 0 +#define AUTO_RPC_PARAMETER_REFERENCE_THRESHOLD 0 +#define AUTO_RPC_ABI AUTO_RPC_ABI_PPC + + +#else +#ifdef __GNUC__ +// gcc won't implemented message - so use #warning +#warning Unknown Architecture +#else +#pragma message( Unknown architecture ) +#endif + +// defining AUTO_RPC_ABI_NONE, creates stub code that fails +#define AUTO_RPC_ABI AUTO_RPC_ABI_NONE +#endif + +// +// Calling convention - we need to be explict on WIN32, so we do that here. Everybody else +// has only one fixed, calling convention. +// +#ifdef _WIN32 +#define AUTO_RPC_CALLSPEC WINAPIV +#else +#define AUTO_RPC_CALLSPEC +#endif + +// +// useful macros; could be rewritten inline/inline templates +// +#define AUTO_RPC__ALIGN_P2( len, bytes ) ( ( len + bytes - 1 ) & ~( bytes - 1 ) ) + +// Return len rounded-up to an integral number of sizeof(type) - provided sizeof(type) is a power of 2 +#define AUTO_RPC_ALIGN_P2( len, type ) AUTO_RPC__ALIGN_P2( len, sizeof( type ) ) + +// Return ptr to end of 'array[xxx]' +#define AUTO_RPC_ARRAY_END( array ) &array[ sizeof( array ) / sizeof( array[0] ) ] + +// strip floating point params if there are no float regs. +#if !AUTO_RPC_FLOAT_REG_PARAMS + +#if AUTO_RPC_CREATE_FLOAT_MAP +#undef AUTO_RPC_CREATE_FLOAT_MAP +#define AUTO_RPC_CREATE_FLOAT_MAP 0 +#endif + +#if AUTO_RPC_ALLOC_SEPARATE_FLOATS +#undef AUTO_RPC_ALLOC_SEPARATE_FLOATS +#define AUTO_RPC_ALLOC_SEPARATE_FLOATS 0 +#endif + +#endif // FLOAT_REG_PARAM + +#define AUTO_RPC_REF_ALIGN 16 // some structures must be memory aligned to 16 bytes, even, on 32-bit systems. +// stack simialrly must be aligned +#define AUTO_RPC_STACK_PADDING ( sizeof( NaturalWord ) / AUTO_RPC_REF_ALIGN ) + + +// defining these externally makes the code a hell of a lot more readable. +#ifdef USE_VARADIC_CALL + +#define AUTO_RPC_NW_3 ... +#define AUTO_RPC_NW_6 ... +#define AUTO_RPC_NW_9 ... +#define AUTO_RPC_NW_12 ... +#define AUTO_RPC_NW_64 ... + +#else +#define AUTO_RPC_NW_3 NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_6 NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_9 NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_5 NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_4_9 AUTO_RPC_NW_5 + +#define AUTO_RPC_NW_12 NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_32 NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_64 NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_60 NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord,\ + NaturalWord,NaturalWord,NaturalWord,NaturalWord + +#define AUTO_RPC_NW_4_64 AUTO_RPC_NW_60 + +#endif // USE VARADIC + +#define AUTO_RPC_INT_ARGS_3( call ) call.intParams[0],call.intParams[1],call.intParams[2] + +#define AUTO_RPC_INT_ARGS_6( call ) call.intParams[0],call.intParams[1],call.intParams[2],call.intParams[3],\ + call.intParams[4],call.intParams[5] + +#define AUTO_RPC_INT_ARGS_9( call ) call.intParams[0],call.intParams[1],call.intParams[2],call.intParams[3],\ + call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8] + +#define AUTO_RPC_INT_ARGS_4_9( call ) call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8] + +#define AUTO_RPC_INT_ARGS_12( call ) call.intParams[0],call.intParams[1],call.intParams[2],call.intParams[3],\ + call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8],call.intParams[9],call.intParams[10],call.intParams[11] + +#define AUTO_RPC_INT_ARGS_32( call ) call.intParams[0],call.intParams[1],call.intParams[2],call.intParams[3],\ + call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8],call.intParams[9],call.intParams[10],call.intParams[11],\ + call.intParams[12],call.intParams[13],call.intParams[14],call.intParams[15],\ + call.intParams[16],call.intParams[17],call.intParams[18],call.intParams[19],\ + call.intParams[20],call.intParams[21],call.intParams[22],call.intParams[23],\ + call.intParams[24],call.intParams[25],call.intParams[26],call.intParams[27],\ + call.intParams[28],call.intParams[29],call.intParams[30],call.intParams[31] + +#define AUTO_RPC_INT_ARGS_4_64( call ) call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8],call.intParams[9],call.intParams[10],call.intParams[11],\ + call.intParams[12],call.intParams[13],call.intParams[14],call.intParams[15],\ + call.intParams[16],call.intParams[17],call.intParams[18],call.intParams[19],\ + call.intParams[20],call.intParams[21],call.intParams[22],call.intParams[23],\ + call.intParams[24],call.intParams[25],call.intParams[26],call.intParams[27],\ + call.intParams[28],call.intParams[29],call.intParams[30],call.intParams[31],\ + call.intParams[32],call.intParams[33],call.intParams[34],call.intParams[35],\ + call.intParams[36],call.intParams[37],call.intParams[38],call.intParams[39],\ + call.intParams[40],call.intParams[41],call.intParams[42],call.intParams[43],\ + call.intParams[44],call.intParams[45],call.intParams[46],call.intParams[47],\ + call.intParams[48],call.intParams[49],call.intParams[50],call.intParams[51],\ + call.intParams[52],call.intParams[53],call.intParams[54],call.intParams[55],\ + call.intParams[56],call.intParams[57],call.intParams[58],call.intParams[59],\ + call.intParams[60],call.intParams[61],call.intParams[62],call.intParams[63] + +#define AUTO_RPC_INT_ARGS_64( call ) call.intParams[0],call.intParams[1],call.intParams[2],call.intParams[3],\ + call.intParams[4],call.intParams[5],call.intParams[6],call.intParams[7],\ + call.intParams[8],call.intParams[9],call.intParams[10],call.intParams[11],\ + call.intParams[12],call.intParams[13],call.intParams[14],call.intParams[15],\ + call.intParams[16],call.intParams[17],call.intParams[18],call.intParams[19],\ + call.intParams[20],call.intParams[21],call.intParams[22],call.intParams[23],\ + call.intParams[24],call.intParams[25],call.intParams[26],call.intParams[27],\ + call.intParams[28],call.intParams[29],call.intParams[30],call.intParams[31],\ + call.intParams[32],call.intParams[33],call.intParams[34],call.intParams[35],\ + call.intParams[36],call.intParams[37],call.intParams[38],call.intParams[39],\ + call.intParams[40],call.intParams[41],call.intParams[42],call.intParams[43],\ + call.intParams[44],call.intParams[45],call.intParams[46],call.intParams[47],\ + call.intParams[48],call.intParams[49],call.intParams[50],call.intParams[51],\ + call.intParams[52],call.intParams[53],call.intParams[54],call.intParams[55],\ + call.intParams[56],call.intParams[57],call.intParams[58],call.intParams[59],\ + call.intParams[60],call.intParams[61],call.intParams[62],call.intParams[63] + +#if AUTO_RPC_ALLOC_SEPARATE_FLOATS +#if AUTO_RPC_FLOAT_REG_PARAMS == 8 + +#define AUTO_RPC_FLOAT_REG_TYPE HardwareReal,HardwareReal,HardwareReal,HardwareReal,\ + HardwareReal,HardwareReal,HardwareReal,HardwareReal +#define AUTO_RPC_FLOAT_REG_ARGS( a ) a.realParams[0],a.realParams[1],a.realParams[2],a.realParams[3],\ + a.realParams[4],a.realParams[5],a.realParams[6],a.realParams[7] + +#elif AUTO_RPC_FLOAT_REG_PARAMS == 4 + +#define AUTO_RPC_FLOAT_REG_TYPE HardwareReal,HardwareReal,HardwareReal,HardwareReal +#define AUTO_RPC_FLOAT_REG_ARGS( a ) a.realParams[0],a.realParams[1],a.realParams[2],a.realParams[3] + +#elif AUTO_RPC_FLOAT_REG_PARAMS == 13 + +#define AUTO_RPC_FLOAT_REG_TYPE HardwareReal,HardwareReal,HardwareReal,HardwareReal,\ + HardwareReal,HardwareReal,HardwareReal,HardwareReal,\ + HardwareReal,HardwareReal,HardwareReal,HardwareReal,HardwareReal +#define AUTO_RPC_FLOAT_REG_ARGS( a ) a.realParams[0],a.realParams[1],a.realParams[2],a.realParams[3],\ + a.realParams[4],a.realParams[5],a.realParams[6],a.realParams[7],\ + a.realParams[8],a.realParams[9],a.realParams[10],a.realParams[11],a.realParams[12] + +#elif AUTO_RPC_FLOAT_REG_PARAMS +#error Need FLOAT_REG_TYPE and AUTO_RPC_FLOAT_REG_ARGS setup +#endif + + +#endif // AUTO_RPC_ALLOC_SEPARATE_FLOATS + +/// \internal +/// Writes number of parameters to push on the stack +void SerializeHeader(char *&out, unsigned int numParams); + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +unsigned int BuildStack(char *stack); + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, + bool es1=true) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 1); + PushHeader(stackPtr, p1, es1); + Push( stackPtr, p1, es1 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, + bool es1=true, bool es2=true) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 2); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, + bool es1=true, bool es2=true, bool es3=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 3); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, P4 p4, + bool es1=true, bool es2=true, bool es3=true, bool es4=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 4); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + PushHeader(stackPtr, p4, es4); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + Push( stackPtr, p4, es4 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, + bool es1=true, bool es2=true, bool es3=true, bool es4=true, bool es5=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 5); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + PushHeader(stackPtr, p4, es4); + PushHeader(stackPtr, p5, es5); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + Push( stackPtr, p4, es4 ); + Push( stackPtr, p5, es5 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, + bool es1=true, bool es2=true, bool es3=true, bool es4=true, bool es5=true, bool es6=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 6); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + PushHeader(stackPtr, p4, es4); + PushHeader(stackPtr, p5, es5); + PushHeader(stackPtr, p6, es6); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + Push( stackPtr, p4, es4 ); + Push( stackPtr, p5, es5 ); + Push( stackPtr, p6, es6 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, + bool es1=true, bool es2=true, bool es3=true, bool es4=true, bool es5=true, bool es6=true, bool es7=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 7); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + PushHeader(stackPtr, p4, es4); + PushHeader(stackPtr, p5, es5); + PushHeader(stackPtr, p6, es6); + PushHeader(stackPtr, p7, es7); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + Push( stackPtr, p4, es4 ); + Push( stackPtr, p5, es5 ); + Push( stackPtr, p6, es6 ); + Push( stackPtr, p7, es7 ); + return (unsigned int)(stackPtr-stack); +} + +/// Builds up a function call and all parameters onto a stack +/// \param[out] Destination stack, which must be big enough to hold all parameters +template +unsigned int BuildStack(char *stack, P1 p1, P2 p2, P3 p3, P4 p4, P5 p5, P6 p6, P7 p7, P8 p8, + bool es1=true, bool es2=true, bool es3=true, bool es4=true, bool es5=true, bool es6=true, bool es7=true, bool es8=true ) +{ + char *stackPtr = (char*) stack; + SerializeHeader(stackPtr, 8); + PushHeader(stackPtr, p1, es1); + PushHeader(stackPtr, p2, es2); + PushHeader(stackPtr, p3, es3); + PushHeader(stackPtr, p4, es4); + PushHeader(stackPtr, p5, es5); + PushHeader(stackPtr, p6, es6); + PushHeader(stackPtr, p7, es7); + PushHeader(stackPtr, p8, es8); + Push( stackPtr, p1, es1 ); + Push( stackPtr, p2, es2 ); + Push( stackPtr, p3, es3 ); + Push( stackPtr, p4, es4 ); + Push( stackPtr, p5, es5 ); + Push( stackPtr, p6, es6 ); + Push( stackPtr, p7, es7 ); + Push( stackPtr, p8, es8 ); + return (unsigned int)(stackPtr-stack); +} + +/// \internal +template +void Push( char*& p, item const i, bool doEndianSwap ) { + memcpy( (void*)p, (void*)&i, sizeof( i ) ); + if (doEndianSwap && RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) p,sizeof( i )); + p += sizeof( i ); +} + +/// \internal +template +void Push( char*& p, item*const i, bool doEndianSwap) { + memcpy( (void*)p, (void*)i, sizeof( *i ) ); + if (doEndianSwap && RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) p,sizeof( i )); + p += sizeof( *i ); +} + +/// \internal +template +void Push( char*& p, item const*const i, bool doEndianSwap) { + memcpy( (void*)p, (void*)i, sizeof( *i ) ); + if (doEndianSwap && RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) p,sizeof( i )); + p += sizeof( *i ); +} + +/// \internal +void Push( char*& p, char*const i, bool doEndianSwap); + +/// \internal +void Push( char*& p, const char*const i, bool doEndianSwap ); + +// THIS STRUCTURE LAYOUT IS HARDCODED INTO THE ASSEMBLY. Unfortunately, that appears to be the +// only way to do it. +struct CallParams { +#if AUTO_RPC_ABI +#if AUTO_RPC_FLOAT_REG_PARAMS + // on most platforms, just a bool telling us whether we need any floats. + unsigned numRealParams; + +#if AUTO_RPC_CREATE_FLOAT_MAP + // + // bitmask: bit(n) set indicate parameter n is a float, not an int. + // + unsigned realMap; +#endif + + // N.B. these may not have type HardwareReal - they're not promoted or converted. +#if AUTO_RPC_ALLOC_SEPARATE_FLOATS + HardwareReal realParams[ AUTO_RPC_FLOAT_REG_PARAMS ]; +#endif + +#endif // AUTO_RPC_FLOAT_REG_PARAMS + + unsigned numIntParams; +#if !AUTO_RPC_ALLOC_SEPARATE_FLOATS && AUTO_RPC_FLOAT_REG_PARAMS && AUTO_RPC_CREATE_FLOAT_MAP + union { + HardwareReal realParams[ AUTO_RPC_FLOAT_REG_PARAMS ]; +#endif + NaturalWord intParams[ ( AUTO_RPC_MAX_PARAMS > AUTO_RPC_INT_REG_PARAMS ? AUTO_RPC_MAX_PARAMS : AUTO_RPC_INT_REG_PARAMS ) + AUTO_RPC_STACK_PADDING ]; + +#if !AUTO_RPC_ALLOC_SEPARATE_FLOATS && AUTO_RPC_FLOAT_REG_PARAMS && AUTO_RPC_CREATE_FLOAT_MAP + }; +#endif + + char refParams[ AUTO_RPC_MAX_PARAMS * AUTO_RPC_REF_ALIGN ]; +#endif // AUTO_RPC_ABI +}; + +/// Given a stack, the length of the stack, a possible last parameter, and a possible this pointer, build a call to a C or C++ function +bool DeserializeParametersAndBuildCall( + CallParams &call, + char *in, unsigned int inLength, + void *lastParam, void *thisPtr); + +// Given the output of DeserializeParametersAndBuildCall, actually call a function +bool CallWithStack( CallParams& call, void *functionPtr ); + +/// \internal +/// functions to return the size of the item. +template +size_t D_size( item const ) { return sizeof( item ); } + +/// \internal +/// functions to return the size of the item. +template +size_t D_size( item const*const ) { return sizeof( item ); } + +/// \internal +/// functions to return the size of the item. +template +size_t D_size( item*const ) { return sizeof( item ); } + +/// \internal +size_t D_size( char*const str ); +/// \internal +size_t D_size( char const*const str ); + +/// \internal +enum { + // to maintain binary compatibility with a historical decision, bit 1 is not used + // in defining the "well known param" types + PARAM_TYPE_MASK = 0x5, + INT_PARAM = 0, // pass by value an integer or structure composed of integers. + REAL_PARAM = 1, // pass by value a SINGLE floating point parameter. + REF_PARAM = 4, // pass a pointer or reference to data which must be aligned. + STR_PARAM = 5, // pass a pointer to this data, which need not be unaligned; + // but MUST be null terminated. + // OBJECT_PARAM = 8, // TODO: pass by value an object, object id as first uint32_t of serialized data? + // OBJECT_REF_PARAM = 9, // TODO: pass by reference an object, object id as first uint32_t of serialized data? + // SC == "Shift count" (Bit index); which is always useful. + ENDIAN_SWAP_SC = 1, DO_ENDIAN_SWAP = 1 << ENDIAN_SWAP_SC, + + RESERVED_BITS = 0xf8, +}; + +/// \internal +template +unsigned D_type( item const ) { return INT_PARAM; } + +/// \internal +template +unsigned D_type( item const*const ) { return REF_PARAM; } + +/// \internal +template +unsigned D_type( item*const ) { return REF_PARAM; } + +/// \internal +unsigned D_type( const char*const ); +/// \internal +unsigned D_type( char*const ); + +/// \internal +unsigned D_type( float ); +/// \internal +unsigned D_type( double ); +/// \internal +unsigned D_type( long double ); + +/// \internal +template +void PushHeader( char*& p, item const i, bool endianSwap ) { + unsigned int s = (unsigned int) D_size( i ); + unsigned char f = D_type( i ) | ( ((int) endianSwap) << ENDIAN_SWAP_SC ); + Push( p, s, endianSwap ); + Push( p, f, false ); +} + +} + +#endif diff --git a/Multiplayer/raknet/GetTime.h b/Multiplayer/raknet/GetTime.h new file mode 100644 index 000000000..8beaf5b4e --- /dev/null +++ b/Multiplayer/raknet/GetTime.h @@ -0,0 +1,39 @@ +/// \file +/// \brief Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __GET_TIME_H +#define __GET_TIME_H + +#include "Export.h" +#include "RakNetTime.h" // For RakNetTime + +/// The namespace RakNet is not consistently used. It's only purpose is to avoid compiler errors for classes whose names are very common. +/// For the most part I've tried to avoid this simply by using names very likely to be unique for my classes. +namespace RakNet +{ + /// Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time. + // Should be renamed GetTimeMS + RakNetTime RAK_DLL_EXPORT GetTime( void ); + // Should be renamed GetTimeUS + RakNetTimeNS RAK_DLL_EXPORT GetTimeNS( void ); + + // Renames, for RakNet 4 + inline RakNetTime RAK_DLL_EXPORT GetTimeMS( void ) {return GetTime();} + inline RakNetTimeNS RAK_DLL_EXPORT GetTimeUS( void ) {return GetTimeNS();} +} + +#endif diff --git a/Multiplayer/raknet/GridSectorizer.h b/Multiplayer/raknet/GridSectorizer.h new file mode 100644 index 000000000..47578ffee --- /dev/null +++ b/Multiplayer/raknet/GridSectorizer.h @@ -0,0 +1,68 @@ +#ifndef _GRID_SECTORIZER_H +#define _GRID_SECTORIZER_H + +//#define _USE_ORDERED_LIST + +#include "RakMemoryOverride.h" + +#ifdef _USE_ORDERED_LIST +#include "DS_OrderedList.h" +#else +#include "DS_List.h" +#endif + +class GridSectorizer +{ +public: + GridSectorizer(); + ~GridSectorizer(); + + // _cellWidth, _cellHeight is the width and height of each cell in world units + // minX, minY, maxX, maxY are the world dimensions (can be changed to dynamically allocate later if needed) + void Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY); + + // Adds a pointer to the grid with bounding rectangle dimensions + void AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + +#ifdef _USE_ORDERED_LIST + + // Removes a pointer, as above + void RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY); + + // Adds and removes in one pass, more efficient than calling both functions consecutively + void MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY, + const float destMinX, const float destMinY, const float destMaxX, const float destMaxY); + +#endif + + // Adds to intersectionList all entries in a certain radius + void GetEntries(DataStructures::List& intersectionList, const float minX, const float minY, const float maxX, const float maxY); + + void Clear(void); + +protected: + int WorldToCellX(const float input) const; + int WorldToCellY(const float input) const; + int WorldToCellXOffsetAndClamped(const float input) const; + int WorldToCellYOffsetAndClamped(const float input) const; + + // Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries + bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const; + + float cellOriginX, cellOriginY; + float cellWidth, cellHeight; + float invCellWidth, invCellHeight; + float gridWidth, gridHeight; + int gridCellWidthCount, gridCellHeightCount; + + + // int gridWidth, gridHeight; + +#ifdef _USE_ORDERED_LIST + DataStructures::OrderedList* grid; +#else + DataStructures::List* grid; +#endif +}; + +#endif diff --git a/Multiplayer/raknet/HTTPConnection.h b/Multiplayer/raknet/HTTPConnection.h new file mode 100644 index 000000000..ee0905b04 --- /dev/null +++ b/Multiplayer/raknet/HTTPConnection.h @@ -0,0 +1,127 @@ +/// \file +/// \brief Contains HTTPConnection, used to communicate with web servers +/// +/// This file is part of RakNet Copyright 2008 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __HTTP_CONNECTION +#define __HTTP_CONNECTION + +#include "Export.h" +#include "RakString.h" +#include "RakMemoryOverride.h" +#include "RakNetTypes.h" +#include "DS_Queue.h" + +class TCPInterface; +struct SystemAddress; + +/// \brief Use HTTPConnection to communicate with a web server. +/// Start an instance of TCPInterface via the Start() command. +/// Instantiate a new instance of HTTPConnection, and associate TCPInterface with the class in the constructor. +/// Use Post() to send commands to the web server, and ProcessDataPacket() to update the connection with packets returned from TCPInterface that have the system address of the web server +/// This class will handle connecting and reconnecting as necessary. +/// +/// Note that only one Post() can be handled at a time. +class RAK_DLL_EXPORT HTTPConnection +{ +public: + /// Returns a HTTP object associated with this tcp connection + /// \pre tcp should already be started + HTTPConnection(TCPInterface& tcp, const char *host, unsigned short port=80); + virtual ~HTTPConnection(); + + /// Submit data to the HTTP server + /// HTTP only allows one request at a time per connection + /// + /// \pre IsBusy()==false + /// \param path the path on the remote server you want to POST to. For example "mywebpage/index.html" + /// \param data A NULL terminated string to submit to the server + /// \param contentType "Content-Type:" passed to post. + void Post(const char *path, const char *data, const char *_contentType="application/x-www-form-urlencoded"); + + /// Get data returned by the HTTP server + /// If IsFinished()==false then this may be empty or a partial + /// response. + RakNet::RakString Read(void); + + /// Call periodically to do time-based updates + void Update(void); + + /// Returns the address of the server we are connected to + SystemAddress GetServerAddress(void) const; + + /// Process an HTTP data packet returned from TCPInterface + /// Returns true when we have gotten all the data from the HTTP server. + /// If this returns true then it's safe to Post() another request + /// Deallocate the packet as usual via TCPInterface + /// \param packet NULL or a packet associated with our host and port + /// \return true when all data from one Post() has been read. + bool ProcessFinalTCPPacket(Packet *packet); + + /// Results of HTTP requests. Standard response codes are < 999 + /// ( define HTTP codes and our internal codes as needed ) + enum ResponseCodes { NoBody=1001, OK=200, Deleted=1002 }; + + HTTPConnection& operator=(const HTTPConnection& rhs){(void) rhs; return *this;} + + /// Encapsulates a raw HTTP response and response code + struct BadResponse + { + public: + BadResponse() {code=0;} + + BadResponse(const unsigned char *_data, int _code) + : data((const char *)_data), code(_code) {} + + BadResponse(const char *_data, int _code) + : data(_data), code(_code) {} + + operator int () const { return code; } + + RakNet::RakString data; + int code; // ResponseCodes + }; + + /// Queued events of failed exchanges with the HTTP server + bool HasBadResponse(int *code, RakNet::RakString *data); + + /// Returns false if the connection is not doing anything else + bool IsBusy(void) const; + + /// \internal + int GetState(void) const; + +private: + SystemAddress server; + TCPInterface& tcp; + RakNet::RakString host; + unsigned short port; + enum { RAK_HTTP_INITIAL, + RAK_HTTP_STARTING, + RAK_HTTP_CONNECTING, + RAK_HTTP_ESTABLISHED, + RAK_HTTP_REQUEST_SENT, + RAK_HTTP_IDLE } state; + RakNet::RakString outgoing, incoming, path, contentType; + DataStructures::Queue badResponses; + void Process(Packet *packet); // the workhorse + + // this helps check the various status lists in TCPInterface + typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void); + bool InList(StatusCheckFunction func); +}; + +#endif + diff --git a/Multiplayer/raknet/IncrementalReadInterface.h b/Multiplayer/raknet/IncrementalReadInterface.h new file mode 100644 index 000000000..9757607a1 --- /dev/null +++ b/Multiplayer/raknet/IncrementalReadInterface.h @@ -0,0 +1,20 @@ +#ifndef __INCREMENTAL_READ_INTERFACE_H +#define __INCREMENTAL_READ_INTERFACE_H + +#include "FileListNodeContext.h" +#include "Export.h" + +class RAK_DLL_EXPORT IncrementalReadInterface +{ +public: + /// Read part of a file into \a destination + /// Return the number of bytes written. Return 0 when file is done. + /// \param[in] filename Filename to read + /// \param[in] startReadBytes What offset from the start of the file to read from + /// \param[in] numBytesToRead How many bytes to read. This is also how many bytes have been allocated to preallocatedDestination + /// \param[out] preallocatedDestination Write your data here + /// \return The number of bytes read, or 0 if none + virtual unsigned int GetFilePart( char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context); +}; + +#endif diff --git a/Multiplayer/raknet/InlineFunctor.h b/Multiplayer/raknet/InlineFunctor.h new file mode 100644 index 000000000..364493562 --- /dev/null +++ b/Multiplayer/raknet/InlineFunctor.h @@ -0,0 +1,61 @@ +#include "FunctionThread.h" +#include "DS_List.h" + +class InlineFunctorProcessor; + +/// A base class to derive functors from for the InlineFunctorProcessor system +class InlineFunctor : public RakNet::Functor +{ +protected: + /// \internal - Calls InlineFunctorProcessor to signal that the functor is done + virtual void HandleResult(bool wasCancelled, void *context); + + /// Tracks the call depth when this functor was pushed. It allows the system to return from functions in the correct order + int callDepth; + + /// Pointer to the calling instance of InlineFunctorProcessor + InlineFunctorProcessor *ifp; + friend class InlineFunctorProcessor; +}; + +/// A base class that will allow you to call YieldOnFunctor() from within a function, and continue with that function when the asynchronous processing has completed +class InlineFunctorProcessor +{ +public: + InlineFunctorProcessor(); + ~InlineFunctorProcessor(); + + /// Start the threads. Should call this first + /// \param[in] numThreads How many worker threads to start + /// \note If only one thread is started, then the calls to YieldOnFunctor will process in that order + void StartThreads(int numThreads); + + /// Stop the threads + /// \param[in] blockOnCurrentProcessing Wait for the current processing to finish? + void StopThreads(bool blockOnCurrentProcessing); + + /// Yield processing in the current function, continuing with the function implemented by CallYieldFunction + /// When the functor completes, this function will return and the caller will continue processing + /// \param[in] inlineFunctor A class that implements Functor::Process() to perform processing that can work asynchronously, such as loading a file or doing a database call + void YieldOnFunctor(InlineFunctor *inlineFunctor); + + /// \internal + /// If the functor is done, continue processing the caller + /// \return True if the topmost functor has completed, false otherwise + bool UpdateIFP(void); + + /// \internal + /// Notify the caller that the functor is done + void Pop(int threadCallDepth); + +protected: + + /// Returns the number of functors that were passed to the system + unsigned GetCallDepth(void) const; + + /// Used to create a thread that processes functors + RakNet::FunctionThread functionThread; + + /// Tracks which threads have been completed + DataStructures::List completedThreads; +}; diff --git a/Multiplayer/raknet/InternalPacket.h b/Multiplayer/raknet/InternalPacket.h new file mode 100644 index 000000000..3518c2281 --- /dev/null +++ b/Multiplayer/raknet/InternalPacket.h @@ -0,0 +1,93 @@ +/// \file +/// \brief \b [Internal] A class which stores a user message, and all information associated with sending and receiving that message. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __INTERNAL_PACKET_H +#define __INTERNAL_PACKET_H + +#include "PacketPriority.h" +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" + +typedef unsigned short SplitPacketIdType; +typedef unsigned int SplitPacketIndexType; + + +/// This is the counter used for holding packet numbers, so we can detect duplicate packets. It should be large enough that if the variables +/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! +/// Use the smallest possible value, such that you send no more than rangeof(MessageNumberType) / GetTimeoutTime() packets per second +/// For the default value of 10 seconds, this is +/// unsigned char - 25.5 packets per second +/// unsigned short - 6553.5 packets per second +/// unsigned int - You'll run out of memory first. +typedef unsigned int MessageNumberType; + +/// This is the counter used for holding ordered packet numbers, so we can detect out-of-order packets. It should be large enough that if the variables +/// were to wrap, the newly wrapped values would no longer be in use. Warning: Too large of a value wastes bandwidth! +typedef MessageNumberType OrderingIndexType; + +typedef RakNetTime RemoteSystemTimeType; + +/// Holds a user message, and related information +/// Don't use a constructor or destructor, due to the memory pool I am using +struct InternalPacket// +{ + ///True if this is an acknowledgment packet + //bool isAcknowledgement; + + /// A unique numerical identifier given to this user message. Used to identify messages on the network + MessageNumberType messageNumber; + /// Identifies the order in which this number was sent. Used locally + MessageNumberType messageInternalOrder; + /// Has this message number been assigned yet? We don't assign until the message is actually sent. + /// This fixes a bug where pre-determining message numbers and then sending a message on a different channel creates a huge gap. + /// This causes performance problems and causes those messages to timeout. + bool messageNumberAssigned; + /// Used only for tracking packetloss and windowing internally, this is the aggreggate packet number that a message was last sent in + unsigned packetNumber; + /// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update. +// bool allowWindowUpdate; + ///The priority level of this packet + PacketPriority priority; + ///What type of reliability algorithm to use with this packet + PacketReliability reliability; + ///What ordering channel this packet is on, if the reliability type uses ordering channels + unsigned char orderingChannel; + ///The ID used as identification for ordering channels + OrderingIndexType orderingIndex; + ///The ID of the split packet, if we have split packets. This is the maximum number of split messages we can send simultaneously per connection. + SplitPacketIdType splitPacketId; + ///If this is a split packet, the index into the array of subsplit packets + SplitPacketIndexType splitPacketIndex; + ///The size of the array of subsplit packets + SplitPacketIndexType splitPacketCount; + ///When this packet was created + RakNetTimeNS creationTime; + ///The next time to take action on this packet + RakNetTimeNS nextActionTime; + // If this was a reliable packet, it included the ping time, to be sent back in an ack + //RakNetTimeNS remoteSystemTime; + //RemoteSystemTimeType remoteSystemTime; + ///How many bits the data is + BitSize_t dataBitLength; + ///Buffer is a pointer to the actual data, assuming this packet has data at all + unsigned char *data; + // How many attempts we made at sending this message + unsigned char timesSent; +}; + +#endif + diff --git a/Multiplayer/raknet/Itoa.h b/Multiplayer/raknet/Itoa.h new file mode 100644 index 000000000..28988219f --- /dev/null +++ b/Multiplayer/raknet/Itoa.h @@ -0,0 +1,8 @@ +#ifndef __RAK_ITOA_H +#define __RAK_ITOA_H + +#include "Export.h" + +RAK_DLL_EXPORT char* Itoa( int value, char* result, int base ); + +#endif diff --git a/Multiplayer/raknet/Kbhit.h b/Multiplayer/raknet/Kbhit.h new file mode 100644 index 000000000..69da0ba31 --- /dev/null +++ b/Multiplayer/raknet/Kbhit.h @@ -0,0 +1,84 @@ +/***************************************************************************** +kbhit() and getch() for Linux/UNIX +Chris Giese http://my.execpc.com/~geezer +Release date: ? +This code is public domain (no copyright). +You can do whatever you want with it. +*****************************************************************************/ +#if !defined(linux) && !defined(__GNUC__) && !defined(__GCCXML__) +#include /* kbhit(), getch() */ + +#else +#include /* struct timeval, select() */ +/* ICANON, ECHO, TCSANOW, struct termios */ +#include /* tcgetattr(), tcsetattr() */ +#include /* atexit(), exit() */ +#include /* read() */ +#include /* printf() */ +#include /* memcpy */ + +static struct termios g_old_kbd_mode; +/***************************************************************************** +*****************************************************************************/ +static void cooked(void) +{ + tcsetattr(0, TCSANOW, &g_old_kbd_mode); +} +/***************************************************************************** +*****************************************************************************/ +static void raw(void) +{ + static char init; +/**/ + struct termios new_kbd_mode; + + if(init) + return; +/* put keyboard (stdin, actually) in raw, unbuffered mode */ + tcgetattr(0, &g_old_kbd_mode); + memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios)); + new_kbd_mode.c_lflag &= ~(ICANON | ECHO); + new_kbd_mode.c_cc[VTIME] = 0; + new_kbd_mode.c_cc[VMIN] = 1; + tcsetattr(0, TCSANOW, &new_kbd_mode); +/* when we exit, go back to normal, "cooked" mode */ + atexit(cooked); + + init = 1; +} +/***************************************************************************** +*****************************************************************************/ +static int kbhit(void) +{ + struct timeval timeout; + fd_set read_handles; + int status; + + raw(); +/* check stdin (fd 0) for activity */ + FD_ZERO(&read_handles); + FD_SET(0, &read_handles); + timeout.tv_sec = timeout.tv_usec = 0; + status = select(0 + 1, &read_handles, NULL, NULL, &timeout); + if(status < 0) + { + printf("select() failed in kbhit()\n"); + exit(1); + } + return status; +} +/***************************************************************************** +*****************************************************************************/ +static int getch(void) +{ + unsigned char temp; + + raw(); +/* stdin = fd 0 */ + if(read(0, &temp, 1) != 1) + return 0; + return temp; +} +#endif + + diff --git a/Multiplayer/raknet/LightweightDatabaseClient.h b/Multiplayer/raknet/LightweightDatabaseClient.h new file mode 100644 index 000000000..aded7f510 --- /dev/null +++ b/Multiplayer/raknet/LightweightDatabaseClient.h @@ -0,0 +1,88 @@ +/// \file +/// \brief Contains the client interface to the simple database included with RakNet, useful for a server browser or a lobby server. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __LIGHTWEIGHT_DATABASE_CLIENT_H +#define __LIGHTWEIGHT_DATABASE_CLIENT_H + +#include "Export.h" +#include "PluginInterface.h" +#include "LightweightDatabaseCommon.h" + +class RakPeerInterface; +struct Packet; + +/// \defgroup SIMPLE_DATABSE_GROUP LightweightDatabase +/// \ingroup PLUGINS_GROUP + +/// \brief The client interface to the simple database included with RakNet, useful for a server browser or a lobby server. +/// \ingroup SIMPLE_DATABSE_GROUP +class RAK_DLL_EXPORT LightweightDatabaseClient : public PluginInterface +{ +public: + + /// Constructor + LightweightDatabaseClient(); + + /// Destructor + virtual ~LightweightDatabaseClient(); + + /// Sends a query to a remote table. + /// The remote system should return ID_DATABASE_QUERY_REPLY, ID_DATABASE_UNKNOWN_TABLE, or ID_DATABASE_INCORRECT_PASSWORD + /// \note If the remote table uses a password and you send the wrong one you will be silently disconnected and banned for one second. + /// \param[in] tableName String name of the remote table. Case sensitive. + /// \param[in] queryPassword Password to query the remote table, if any. + /// \param[in] columnSubset An array of string names of the columns to query. The resulting table will return the columns in this same order. Pass 0 for all columns. + /// \param[in] numColumnSubset The number of elements in the columnSubset array + /// \param[in] filter Filters to apply to the query. For each row the filters are applied. All filters must be passed for the row to be returned. Pass 0 for no filters. + /// \param[in] numFilters The number of elements in the filter array + /// \param[in] rowIds Which particular rows to return. Pass 0 for all rows. + /// \param[in] numRowIDs The number of elements in the rowIds array + /// \param[in] systemAddress Which system to send to. + /// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send + void QueryTable(const char *tableName, const char *queryPassword, const char **columnNamesSubset, unsigned char numColumnSubset, DatabaseFilter *filter, unsigned char numFilters, unsigned *rowIds, unsigned char numRowIDs, SystemAddress systemAddress, bool broadcast); + + /// Sets one or more values in a new or existing row, assuming the server allows row creation and updates. + /// No response is returned by the server. + /// \param[in] tableName String name of the remote table. Case sensitive. + /// \param[in] updatePassword Password to update the remote table, if any. + /// \param[in] updateMode See RowUpdateMode in LightweightDatabaseCommon.h . This determines if to update an existing or new row. + /// \param[in] hasRowId True if a valid value was passed for \a rowId, false otherwise. If false, will lookup the row by system address. Required if adding a new row and the remote system does not automatically create rowIDs. + /// \param[in] rowId The rowID of the new or existing row. + /// \param[in] cellUpdates An array of DatabaseCellUpdate structures containing the values to write to the remote row. + /// \param[in] numCellUpdates The number of elements in the cellUpdates array + /// \param[in] systemAddress Which system to send to. + /// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send + void UpdateRow(const char *tableName, const char *updatePassword, RowUpdateMode updateMode, bool hasRowId, unsigned rowId, DatabaseCellUpdate *cellUpdates, unsigned char numCellUpdates, SystemAddress systemAddress, bool broadcast); + + /// Removes a remote row, assuming the server allows row removal. + /// No response is returned by the server. + /// \param[in] tableName String name of the remote table. Case sensitive. + /// \param[in] removePassword Password to remove rows from the remote table, if any. + /// \param[in] rowId The rowID of the existing row. + /// \param[in] systemAddress Which system to send to. + /// \param[in] broadcast Broadcast or not. Same as the parameter in RakPeer::Send + void RemoveRow(const char *tableName, const char *removePassword, unsigned rowId, SystemAddress systemAddress, bool broadcast); + + /// \internal For plugin handling + void OnAttach(RakPeerInterface *peer); + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); +protected: + RakPeerInterface *rakPeer; +}; + +#endif diff --git a/Multiplayer/raknet/LightweightDatabaseCommon.h b/Multiplayer/raknet/LightweightDatabaseCommon.h new file mode 100644 index 000000000..3ce375f25 --- /dev/null +++ b/Multiplayer/raknet/LightweightDatabaseCommon.h @@ -0,0 +1,54 @@ +#ifndef __SIMPLE_DATABASE_COMMON_H +#define __SIMPLE_DATABASE_COMMON_H + +#include "DS_Table.h" + +namespace RakNet +{ + class BitStream; +}; + +#define _SIMPLE_DATABASE_PASSWORD_LENGTH 32 +#define _SIMPLE_DATABASE_TABLE_NAME_LENGTH 32 + +#define SYSTEM_ID_COLUMN_NAME "__SystemAddress" +#define SYSTEM_GUID_COLUMN_NAME "__SystemGuid" +#define LAST_PING_RESPONSE_COLUMN_NAME "__lastPingResponseTime" +#define NEXT_PING_SEND_COLUMN_NAME "__nextPingSendTime" + +struct DatabaseFilter +{ + void Serialize(RakNet::BitStream *out); + bool Deserialize(RakNet::BitStream *in); + + DataStructures::Table::Cell cellValue; + DataStructures::Table::FilterQueryType operation; + DataStructures::Table::ColumnType columnType; + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; +}; + +/// The value to write to a cell in a remote database. +struct DatabaseCellUpdate +{ + void Serialize(RakNet::BitStream *out); + bool Deserialize(RakNet::BitStream *in); + + DataStructures::Table::Cell cellValue; + DataStructures::Table::ColumnType columnType; + char columnName[_TABLE_MAX_COLUMN_NAME_LENGTH]; +}; + +enum RowUpdateMode +{ + // Only update an existing row. rowId is required. + RUM_UPDATE_EXISTING_ROW, + + // Update an existing row if present - otherwise add a new row. rowId is required. + RUM_UPDATE_OR_ADD_ROW, + + // Add a new row. If rowId is passed then the row will only be added if this id doesn't already exist. + // If rowId is not passed and the table requires a rowId the creation fails. + RUM_ADD_NEW_ROW, +}; + +#endif diff --git a/Multiplayer/raknet/LightweightDatabaseServer.h b/Multiplayer/raknet/LightweightDatabaseServer.h new file mode 100644 index 000000000..d4d26214f --- /dev/null +++ b/Multiplayer/raknet/LightweightDatabaseServer.h @@ -0,0 +1,149 @@ +/// \file +/// \brief A simple flat database included with RakNet, useful for a server browser or a lobby server. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __LIGHTWEIGHT_DATABASE_SERVER_H +#define __LIGHTWEIGHT_DATABASE_SERVER_H + +#include "Export.h" +#include "PluginInterface.h" +#include "LightweightDatabaseCommon.h" +#include "DS_Map.h" + +class RakPeerInterface; +struct Packet; + +/// \brief A simple flat database included with RakNet, useful for a server browser or a lobby server. +/// A flat database interface. Adds the ability to track IPs of row updaters and passwords for table read and write operations, +/// Best used for data in which queries which do not need to be updated in real-time +/// \ingroup SIMPLE_DATABSE_GROUP +class RAK_DLL_EXPORT LightweightDatabaseServer : public PluginInterface + { + public: + /// Constructor + LightweightDatabaseServer(); + + /// Destructor + virtual ~LightweightDatabaseServer(); + + /// Returns a table by name. + /// It is valid to read and write to the returned pointer. + /// \param[in] tableName The name of the table that was passed to AddTable + /// \return The requested table, or 0 if \a tableName cannot be found. + DataStructures::Table *GetTable(char *tableName); + + /// Adds a new table + /// It is valid to read and write to the returned pointer. + /// \param[in] tableName Name of the new table to create. Remote systems will refer to this table by this name. + /// \param[in] allowRemoteQuery true to allow remote systems to query the table. false to not allow this. + /// \param[in] allowRemoteUpdate true to allow remote systems to update rows in the table. false to not allow this. + /// \param[in] allowRemoteRemove true to allow remote systems to remove rows from the table. false to not allow this. + /// \param[in] queryPassword The password required to query the table. Pass an empty string for no password. + /// \param[in] updatePassword The password required to update rows in the table. Pass an empty string for no password. + /// \param[in] removePassword The password required to remove rows from the table. Pass an empty string for no password. + /// \param[in] oneRowPerSystemAddress Only used if allowRemoteUpdate==true. This limits remote systems to one row. + /// \param[in] onlyUpdateOwnRows Only used if allowRemoteUpdate==true. This limits remote systems to only updating rows they created. + /// \param[in] removeRowOnPingFailure Only used if allowRemoteUpdate==true and removeRowOnDisconnect==false. If true, this will periodically ping disconnected systems and remove rows created by that system if that system does not respond to pings. + /// \param[in] removeRowOnDisconnect Only used if allowRemoteUpdate==true. This removes rows created by a system when that system disconnects. + /// \param[in] autogenerateRowIDs true to automatically generate row IDs. Rows are stored in order by row ID. If false, the clients must specify a unique row ID when adding rows. If they specify a row that already exists the addition is ignored. + /// \return The newly created table, or 0 on failure. + DataStructures::Table* AddTable(char *tableName, + bool allowRemoteQuery, + bool allowRemoteUpdate, + bool allowRemoteRemove, + const char *queryPassword, + const char *updatePassword, + const char *removePassword, + bool oneRowPerSystemAddress, + bool onlyUpdateOwnRows, + bool removeRowOnPingFailure, + bool removeRowOnDisconnect, + bool autogenerateRowIDs); + + /// Removes a table by name. + /// \param[in] tableName The name of the table that was passed to AddTable + /// \return true on success, false on failure. + bool RemoveTable(char *tableName); + + /// Clears all memory. + void Clear(void); + + // Gets the next valid auto-generated rowId for a table and increments it. + unsigned GetAndIncrementRowID(char *tableName); + + /// Returns a linked list of ordered lists containing the rows of a table, by name. + /// The returned structure is internal to the BPlus tree. See DS_BPlusTree + /// This is a convenience accessor, as you can also get this from the table returned from GetTable() + /// \param[in] tableName The name of the table that was passed to AddTable + /// \return The requested rows, or 0 if \a tableName cannot be found. + DataStructures::Page *GetTableRows(char *tableName); + + /// \internal For plugin handling + virtual void OnAttach(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void Update(RakPeerInterface *peer); + /// \internal For plugin handling + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + /// \internal For plugin handling + virtual void OnShutdown(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + + struct DatabaseTable + { + bool allowRemoteUpdate; + bool allowRemoteQuery; + bool allowRemoteRemove; + + char updatePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH]; + char queryPassword[_SIMPLE_DATABASE_PASSWORD_LENGTH]; + char removePassword[_SIMPLE_DATABASE_PASSWORD_LENGTH]; + bool oneRowPerSystemAddress; + bool onlyUpdateOwnRows; + bool removeRowOnPingFailure; + bool removeRowOnDisconnect; + bool autogenerateRowIDs; + char tableName[_SIMPLE_DATABASE_TABLE_NAME_LENGTH]; + + // Used if autogenerateRowIDs==true + unsigned nextRowId; + + unsigned SystemAddressColumnIndex; + unsigned SystemGuidColumnIndex; + unsigned lastPingResponseColumnIndex; + unsigned nextPingSendColumnIndex; + RakNetTime nextRowPingCheck; + DataStructures::Table table; + }; + + static int DatabaseTableComp( char* const &key1, char* const &key2 ); + + protected: + DataStructures::Map database; + void OnQueryRequest(RakPeerInterface *peer, Packet *packet); + void OnUpdateRow(RakPeerInterface *peer, Packet *packet); + void OnRemoveRow(RakPeerInterface *peer, Packet *packet); + void OnPong(RakPeerInterface *peer, Packet *packet); + // mode 0 = query, mode 1 = update, mode 2 = remove + DatabaseTable * DeserializeClientHeader(RakNet::BitStream *inBitstream, RakPeerInterface *peer, Packet *packet, int mode); + DataStructures::Table::Row * GetRowFromIP(DatabaseTable *databaseTable, SystemAddress systemAddress, unsigned *rowId); + bool RowHasIP(DataStructures::Table::Row *row, SystemAddress systemAddress, unsigned SystemAddressColumnIndex); + DataStructures::Table::Row * AddRow(LightweightDatabaseServer::DatabaseTable *databaseTable, SystemAddress systemAddress, RakNetGUID guid, bool hasRowId, unsigned rowId); + void RemoveRowsFromIP(SystemAddress systemAddress); + + }; + +#endif diff --git a/Multiplayer/raknet/LinuxStrings.h b/Multiplayer/raknet/LinuxStrings.h new file mode 100644 index 000000000..8f14fd2b9 --- /dev/null +++ b/Multiplayer/raknet/LinuxStrings.h @@ -0,0 +1,15 @@ +#ifndef _GCC_WIN_STRINGS +#define _GCC_WIN_STRINGS + +#if (defined(__GNUC__) || defined(__GCCXML__) || defined(_PS3)) && !defined(_WIN32) +int _stricmp(const char* s1, const char* s2); +int _strnicmp(const char* s1, const char* s2, size_t n); +char *_strlwr(char * str ); + +#ifndef _vsnprintf +#define _vsnprintf vsnprintf +#endif + +#endif + +#endif diff --git a/Multiplayer/raknet/LobbyClientInterface.h b/Multiplayer/raknet/LobbyClientInterface.h new file mode 100644 index 000000000..f027bcddb --- /dev/null +++ b/Multiplayer/raknet/LobbyClientInterface.h @@ -0,0 +1,125 @@ +#ifndef __LOBBY_CLIENT_INTERFACE_H +#define __LOBBY_CLIENT_INTERFACE_H + +enum LobbyEvent +{ + LOBBY_EVENT_COUNT +}; + +enum LobbyClientEvents +{ + LCE_ADDED_FRIEND, + LCE_SENT_MESSAGE_SUCCESS, + LCE_BLOCK_USER_SUCCESS, + LCE_ALL_FRIENDS_DOWNLOADED, + LCE_FRIEND_REMOVED_FROM_LIST, + LCE_FRIEND_ADDED_DIFFERENT_GAME, + LCE_FRIEND_ADDED_SAME_GAME, + LCE_FRIEND_OFFLINE, + LCE_SAME_GAME_INSTANT_MESSAGE, + LCE_GAME_INVITATION, + LCE_GAME_GOT_MESSAGE_WITH_ATTACHMENT_GUI +}; + +#if defined(WIN32) +#include "[PC]LobbyClientTypes.h" +#else +#include "[PS3]LobbyClientTypes.h" +#endif + +class LobbyClientCBInterface +{ +public: + LobbyClientCBInterface() {} + virtual ~LobbyClientCBInterface() {} + virtual void AddedFriendSuccessfully(void) {} + virtual void SentMessageSuccessfully(void) {} + virtual void BlockedUserSuccessfully(void) {} + virtual void AllFriendsDownloaded(void) {} + virtual void FriendRemovedFromFriendsList(void *friendId, const char *friendName) {} + virtual void FriendAddedToFriendsList(void *friendId, const char *friendName, bool inSameGame) {} + virtual void FriendOffline(void *friendId, const char *friendName) {} + virtual void GotInstantMessage(void *friendId, const char *friendName, char *data, int dataLength) {} + virtual void GotGameInvitation(void *friendId, const char *friendName) {} + virtual void GameMessageWithAttachmentArrived(void *friendId, const char *friendName) {} + virtual void GotGameMessageWithAttachmentThroughGui(void *friendId, const char *friendName, char *data, int dataLength) {} + + virtual void LeaveRoomComplete(void) {} + virtual void NewMemberJoinedRoom(void *memberId, const char *memberName) {} + virtual void MemberLeftRoom(void *memberId, const char *memberName) {} + virtual void RoomDisappeared(void) {} + virtual void RoomOwnershipGranted(void *newOwnerId, const char *newOwnerName) {} + virtual void RoomOwnershipChanged(void *newOwnerId, const char *newOwnerName) {} + virtual void RoomMemberKicked(void *memberId, const char *memberName) {} + virtual void KickedFromRoom(void) {} + virtual void RoomJoined(void) {} + virtual void RoomCreated(void) {} + virtual void RoomSearchJoined(void) {} + virtual void RoomQuickMatchJoined(void) {} + virtual void RoomQuickMatchCancelled(void) {} + virtual void RoomJoinInvitationSentConfirmation(void) {} + virtual void RoomJoinInvitationAccepted(void *roomId) {} + virtual void ReceivedRoomList(void) {} + virtual void ScoreSubmitted(unsigned int newRank); + virtual void GotScore(void *memberId, char *onlineName, unsigned int rank, unsigned int highestRank, unsigned long long score ); + +}; + +class LobbyClientInterface +{ +public: + virtual bool StartLogin(const char *titleIdentifier){return false;} + virtual void Logoff(){} + virtual bool IsLoggedIn(void) const{return false;} + virtual bool IsFriendsListOpen(void) const{return false;} + virtual bool IsSendingMessage(void) const{return false;} + virtual bool IsReceivingStoredMessage(void) const{return false;} + virtual bool IsAddingFriend(void) const{return false;} + virtual bool IsCreatingRoom(void) const{return false;} + virtual bool IsSearchingAndJoiningRoom(void) const{return false;} + virtual bool IsDoingQuickMatch(void) const{return false;} + virtual bool IsGettingRoomList(void) const{return false;} + virtual bool IsInRoom(void) const{return false;} + virtual bool RecordScore(unsigned int scoreboardId, unsigned long long int score, const char *censoredComment, const char *gameData, unsigned int gamedataLength){return false;} + virtual bool GetScoreRanking(unsigned int scoreboardId, LobbyClientUserId* userId){return false;} + virtual void AbortScoreTransaction(void){} + virtual void Update(void){} + virtual bool ShowFriendsList(void){return false;} + virtual bool AddFriend(LobbyClientUserId* to, const char *body){return false;} + virtual unsigned GetNumFriends(void) const{return 0;} + virtual const LobbyClientUserId& GetFriendIDFromIndex(unsigned idx) const{return LobbyClientUserId();} + virtual bool GetFriendSameGameFromIndex(unsigned idx) const{return false;} + virtual unsigned GetIndexFromFriendId(LobbyClientUserId* id){return 0;} + virtual bool SendGameMessage(LobbyClientUserId* to, const void *data, int dataSize){return false;} + virtual bool SendStoredMessage(LobbyClientUserId* to, const char *subject, const char *body, const void *data, int dataSize){return false;} + virtual bool ReceiveStoredMessageWithAttachment(void){return false;} + virtual bool CreateRoom(int totalSlots, int privateSlots, bool kickMembersOnOwnerLeave, bool allowSearchesForRoom){return false;} + virtual unsigned GetNumRoomMembers(void) const{return 0;} + virtual const LobbyClientUserId& GetRoomMemberIDFromIndex(unsigned idx) const{return LobbyClientUserId();} + virtual unsigned GetIndexFromRoomMemberID(LobbyClientUserId* id) const{return 0;} + virtual unsigned GetNumRooms(void) const{return 0;} + virtual const LobbyClientRoomId & GetRoomIDFromIndex(unsigned idx) const{return LobbyClientRoomId();} + virtual unsigned GetIndexFromRoomID(LobbyClientRoomId *id) const{return 0;} + virtual bool GetRoomList(int requiredSlots){return false;} + virtual void SetRoomStealth(bool isHidden){} + virtual bool LeaveRoom(void){return false;} + virtual bool GrantRoomOwnership(LobbyClientUserId * userId){return false;} + virtual bool KickRoomMember(LobbyClientUserId * userId){return false;} + virtual bool SendRoomInvitation(LobbyClientUserId * userId, const char *subject, const char *body, bool inviteToPrivateSlot){return false;} + virtual bool SearchAndJoinRoom(int requiredSlots){return false;} + virtual bool QuickMatch(int requiredSlots, int timeoutInSeconds){return false;} + virtual bool BlockPlayer(LobbyClientUserId* to){return false;} + virtual unsigned GetNumBlockedPlayers(void) const{return 0;} + virtual const LobbyClientUserId& GetBlockedPlayerAtIndex(unsigned idx){return LobbyClientUserId();} + virtual bool AddToMetPlayerHistory(LobbyClientUserId* id, const char *description){return false;} + virtual void SetCallbackInterface(LobbyClientCBInterface *cbi){} + virtual const LobbyClientUserId* GetMyID(void) const{return 0;} +}; + +#if defined(WIN32) +#include "[PC]LobbyClient.h" +#else +#include "[PS3]LobbyClient.h" +#endif + +#endif diff --git a/Multiplayer/raknet/LogCommandParser.h b/Multiplayer/raknet/LogCommandParser.h new file mode 100644 index 000000000..49b342f74 --- /dev/null +++ b/Multiplayer/raknet/LogCommandParser.h @@ -0,0 +1,117 @@ +/// \file +/// \brief Contains LogCommandParser , Used to send logs to connected consoles +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __LOG_COMMAND_PARSER +#define __LOG_COMMAND_PARSER + +class RakPeerInterface; + +#include "CommandParserInterface.h" +#include "Export.h" + +/// \brief Adds the ability to send logging output to a remote console +class RAK_DLL_EXPORT LogCommandParser : public CommandParserInterface +{ +public: + LogCommandParser(); + ~LogCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, SystemAddress systemAddress); + + /// All logs must be associated with a channel. This is a filter so that remote clients only get logs for a system they care about. + // If you call Log with a channel that is unknown, that channel will automatically be added + /// \param[in] channelName A persistent string naming the channel. Don't deallocate this string. + void AddChannel(const char *channelName); + + /// Write a log to a channel. + /// Logs are not buffered, so only remote consoles connected and subscribing at the time you write will get the output. + /// \param[in] format Same as RAKNET_DEBUG_PRINTF() + /// \param[in] ... Same as RAKNET_DEBUG_PRINTF() + void WriteLog(const char *channelName, const char *format, ...); + + /// A callback for when \a systemAddress has connected to us. + /// \param[in] systemAddress The player that has connected. + /// \param[in] transport The transport interface that sent us this information. Can be used to send messages to this or other players. + void OnNewIncomingConnection(SystemAddress systemAddress, TransportInterface *transport); + + /// A callback for when \a systemAddress has disconnected, either gracefully or forcefully + /// \param[in] systemAddress The player that has disconnected. + /// \param[in] transport The transport interface that sent us this information. + void OnConnectionLost(SystemAddress systemAddress, TransportInterface *transport); + + /// This is called every time transport interface is registered. If you want to save a copy of the TransportInterface pointer + /// This is the place to do it + /// \param[in] transport The new TransportInterface + void OnTransportChange(TransportInterface *transport); +protected: + /// Sends the currently active channels to the user + /// \param[in] systemAddress The player to send to + /// \param[in] transport The transport interface to use to send the channels + void PrintChannels(SystemAddress systemAddress, TransportInterface *transport) const; + + /// Unsubscribe a user from a channel (or from all channels) + /// \param[in] systemAddress The player to unsubscribe to + /// \param[in] channelName If 0, then unsubscribe from all channels. Otherwise unsubscribe from the named channel + unsigned Unsubscribe(SystemAddress systemAddress, const char *channelName); + + /// Subscribe a user to a channel (or to all channels) + /// \param[in] systemAddress The player to subscribe to + /// \param[in] channelName If 0, then subscribe from all channels. Otherwise subscribe to the named channel + unsigned Subscribe(SystemAddress systemAddress, const char *channelName); + + /// Given the name of a channel, return the index into channelNames where it is located + /// \param[in] channelName The name of the channel + unsigned GetChannelIndexFromName(const char *channelName); + + /// One of these structures is created per player + struct SystemAddressAndChannel + { + /// The ID of the player + SystemAddress systemAddress; + + /// Bitwise representations of the channels subscribed to. If bit 0 is set, then we subscribe to channelNames[0] and so on. + unsigned channels; + }; + + /// The list of remote users. Added to when users subscribe, removed when they disconnect or unsubscribe + DataStructures::List 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 diff --git a/Multiplayer/raknet/MTUSize.h b/Multiplayer/raknet/MTUSize.h new file mode 100644 index 000000000..397b041ec --- /dev/null +++ b/Multiplayer/raknet/MTUSize.h @@ -0,0 +1,47 @@ +/// \file +/// \brief \b [Internal] Defines the default maximum transfer unit. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef DEFAULT_MTU_SIZE + +/// The MTU size to use if RakPeer::SetMTUSize() is not called. +/// \remarks I think many people forget to call RakPeer::SetMTUSize() so I'm setting this to 1500 by default for efficiency. +/// \li \em 17914 16 Mbit/Sec Token Ring +/// \li \em 4464 4 Mbits/Sec Token Ring +/// \li \em 4352 FDDI +/// \li \em 1500. The largest Ethernet packet size \b recommended. This is the typical setting for non-PPPoE, non-VPN connections. The default value for NETGEAR routers, adapters and switches. +/// \li \em 1492. The size PPPoE prefers. +/// \li \em 1472. Maximum size to use for pinging. (Bigger packets are fragmented.) +/// \li \em 1468. The size DHCP prefers. +/// \li \em 1460. Usable by AOL if you don't have large email attachments, etc. +/// \li \em 1430. The size VPN and PPTP prefer. +/// \li \em 1400. Maximum size for AOL DSL. +/// \li \em 576. Typical value to connect to dial-up ISPs. +#if defined(_XBOX) || defined(X360) +#define DEFAULT_MTU_SIZE 1264 +#else +#define DEFAULT_MTU_SIZE 1492 +#endif + +/// The largest value for an UDP datagram +/// \sa RakPeer::SetMTUSize() +#if defined(_XBOX) || defined(X360) +#define MAXIMUM_MTU_SIZE 1264 +#else +#define MAXIMUM_MTU_SIZE 1492 +#endif + +#endif diff --git a/Multiplayer/raknet/MessageFilter.h b/Multiplayer/raknet/MessageFilter.h new file mode 100644 index 000000000..dbbd925ed --- /dev/null +++ b/Multiplayer/raknet/MessageFilter.h @@ -0,0 +1,193 @@ +/// \file +/// \brief Message filter plugin. Assigns systems to FilterSets. Each FilterSet limits what messages are allowed. This is a security related plugin. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __MESSAGE_FILTER_PLUGIN_H +#define __MESSAGE_FILTER_PLUGIN_H + +class RakPeerInterface; +#include "RakNetTypes.h" +#include "PluginInterface.h" +#include "DS_OrderedList.h" +#include "Export.h" + +/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do. +#define MESSAGE_FILTER_MAX_MESSAGE_ID 256 + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT MessageFilterStrComp( char *const &key,char *const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilterSet +{ + bool banOnFilterTimeExceed; + bool kickOnDisallowedMessage; + bool banOnDisallowedMessage; + RakNetTime disallowedMessageBanTimeMS; + RakNetTime timeExceedBanTimeMS; + RakNetTime maxMemberTimeMS; + void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData, unsigned char messageID); + void *disallowedCallbackUserData; + void (*timeoutCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData); + void *timeoutUserData; + int filterSetID; + bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID]; + DataStructures::OrderedList allowedRPCs; +}; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT FilterSetComp( const int &key, FilterSet * const &data ); + +/// \internal Has to be public so some of the shittier compilers can use it. +struct FilteredSystem +{ + SystemAddress systemAddress; + FilterSet *filter; + RakNetTime timeEnteredThisSet; +}; + +/// \internal Has to be public so some of the shittier compilers can use it. +int RAK_DLL_EXPORT FilteredSystemComp( const SystemAddress &key, const FilteredSystem &data ); + +/// \defgroup MESSAGEFILTER_GROUP MessageFilter +/// \ingroup PLUGINS_GROUP + +/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed. +/// The MessageFilter plugin is used for security where you limit what systems can send what kind of messages. +/// You implicitly define FilterSets, and add allowed message IDs and RPC calls to these FilterSets. +/// You then add systems to these filters, such that those systems are limited to sending what the filters allows. +/// You can automatically assign systems to a filter. +/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message. +/// Each system is a member of either zero or one filters. +/// Add this plugin before any plugin you wish to filter (most likely just add this plugin before any other). +/// \ingroup MESSAGEFILTER_GROUP +class RAK_DLL_EXPORT MessageFilter : public PluginInterface +{ +public: + MessageFilter(); + virtual ~MessageFilter(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + + /// Automatically add all new systems to a particular filter + /// Defaults to -1 + /// \param[in] filterSetID Which filter to add new systems to. <0 for do not add. + void SetAutoAddNewConnectionsToFilter(int filterSetID); + + /// Allow a range of message IDs + /// Always allowed by default: ID_CONNECTION_REQUEST_ACCEPTED through ID_DOWNLOAD_PROGRESS + /// Usually you specify a range to make it easier to add new enumerations without having to constantly refer back to this function. + /// \param[in] allow True to allow this message ID, false to disallow. By default, all messageIDs except the noted types are disallowed. This includes messages from other plugins! + /// \param[in] messageIDStart The first ID_* message to allow in the range. Inclusive. + /// \param[in] messageIDEnd The last ID_* message to allow in the range. Inclusive. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID); + + /// Allow an RPC function, by name + /// \param[in] allow True to allow an RPC call with this function name, false to disallow. All RPCs are disabled by default. + /// \param[in] functionName the function name of the RPC call. Must match the function name exactly, including case. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetAllowRPC(bool allow, const char *functionName, int filterSetID); + + /// What action to take on a disallowed message. You can kick or not. You can add them to the ban list for some time + /// By default no action is taken. The message is simply ignored. + /// param[in] 0 for permanent ban, >0 for ban time in milliseconds. + /// \param[in] kickOnDisallowed kick the system that sent a disallowed message. + /// \param[in] banOnDisallowed ban the system that sent a disallowed message. See \a banTimeMS for the ban duration + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNetTime banTimeMS, int filterSetID); + + /// Set a user callback to be called on an invalid message for a particular filterSet + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData, unsigned char messageID)); + + /// Set a user callback to be called when a user is disconnected due to SetFilterMaxTime + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + /// \param[in] userData A pointer passed with the callback + /// \param[in] invalidMessageCallback A pointer to a C function to be called back with the specified parameters. + void SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, SystemAddress systemAddress, int filterSetID, void *userData)); + + /// Limit how long a connection can stay in a particular filterSetID. After this time, the connection is kicked and possibly banned. + /// By default there is no limit to how long a connection can stay in a particular filter set. + /// \param[in] allowedTimeMS How many milliseconds to allow a connection to stay in this filter set. + /// \param[in] banOnExceed True or false to ban the system, or not, when \a allowedTimeMS is exceeded + /// \param[in] banTimeMS Passed to the milliseconds parameter of RakPeer::AddToBanList. + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. + void SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNetTime banTimeMS, int filterSetID); + + /// Get the filterSetID a system is using. Returns -1 for none. + /// \param[in] systemAddress The system we are referring to + int GetSystemFilterSet(SystemAddress systemAddress); + + /// Assign a system to a filter set. + /// Systems are automatically added to filter sets (or not) based on SetAutoAddNewConnectionsToFilter() + /// This function is used to change the filter set a system is using, to add it to a new filter set, or to remove it from all existin filter sets. + /// \param[in] systemAddress The system we are referring to + /// \param[in] filterSetID A user defined ID to represent a filter set. If no filter with this ID exists, one will be created with default settings. If -1, the system will be removed from all filter sets. + void SetSystemFilterSet(SystemAddress systemAddress, int filterSetID); + + /// Returns the number of systems subscribed to a particular filter set + /// Using anything other than -1 for \a filterSetID is slow, so you should store the returned value. + /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just returns the total number of filter systems in that case). + unsigned GetSystemCount(int filterSetID) const; + + /// Returns a system subscribed to a particular filter set,by index. + /// index should be between 0 and the GetSystemCount(filterSetID)-1; + /// \param[in] filterSetID The filter set to limit to. Use -1 for none (just indexes all the filtered systems in that case). + /// \param[in] index A number between 0 and GetSystemCount(filterSetID)-1; + SystemAddress GetSystemByIndex(int filterSetID, unsigned index); + + /// Returns the total number of filter sets. + /// \return The total number of filter sets. + unsigned GetFilterSetCount(void) const; + + /// Returns the ID of a filter set, by index + /// \param[in] An index between 0 and GetFilterSetCount()-1 inclusive + int GetFilterSetIDByIndex(unsigned index); + + /// Delete a FilterSet. All systems formerly subscribed to this filter are now unrestricted. + /// \param[in] filterSetID The ID of the filter set to delete. + void DeleteFilterSet(int filterSetID); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void OnAttach(RakPeerInterface *peer); + virtual void OnDetach(RakPeerInterface *peer); + virtual void OnShutdown(RakPeerInterface *peer); + virtual void Update(RakPeerInterface *peer); + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + +protected: + + void Clear(void); + void DeallocateFilterSet(FilterSet *filterSet); + FilterSet* GetFilterSetByID(int filterSetID); + void OnInvalidMessage(RakPeerInterface *peer, FilterSet *filterSet, SystemAddress systemAddress, unsigned char messageID); + + DataStructures::OrderedList filterList; + DataStructures::OrderedList systemList; + + int autoAddNewConnectionsToFilter; +}; + +#endif diff --git a/Multiplayer/raknet/MessageIdentifiers.h b/Multiplayer/raknet/MessageIdentifiers.h index 17cddf01d..58a667c2d 100644 --- a/Multiplayer/raknet/MessageIdentifiers.h +++ b/Multiplayer/raknet/MessageIdentifiers.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -18,6 +18,10 @@ #ifndef __MESSAGE_IDENTIFIERS_H #define __MESSAGE_IDENTIFIERS_H +#if defined(RAKNET_USE_CUSTOM_PACKET_IDS) +#include "CustomPacketIdentifiers.h" +#else + /// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library /// To define your own message id, define an enum following the code example that follows. /// @@ -30,7 +34,7 @@ /// \endcode /// /// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream -enum +enum DefaultMessageIDTypes { // // RESERVED TYPES - DO NOT CHANGE THESE @@ -63,6 +67,9 @@ enum ID_RPC, /// Remote procedure call reply, for RPCs that return data (internal use only) ID_RPC_REPLY, + /// RakPeer - Same as ID_ADVERTISE_SYSTEM, but intended for internal use rather than being passed to the user. Second byte indicates type. Used currently for NAT punchthrough for receiver port advertisement. See ID_NAT_ADVERTISE_RECIPIENT_PORT + ID_OUT_OF_BAND_INTERNAL, + // // USER TYPES - DO NOT CHANGE THESE @@ -70,7 +77,7 @@ enum /// RakPeer - In a client/server environment, our connection request to the server has been accepted. ID_CONNECTION_REQUEST_ACCEPTED, - /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. + /// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect. ID_CONNECTION_ATTEMPT_FAILED, /// RakPeer - Sent a connect request to a system we are currently connected to. ID_ALREADY_CONNECTED, @@ -78,9 +85,9 @@ enum ID_NEW_INCOMING_CONNECTION, /// RakPeer - The system we attempted to connect to is not accepting new connections. ID_NO_FREE_INCOMING_CONNECTIONS, - /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the server has shutdown. + /// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the server has shutdown. ID_DISCONNECTION_NOTIFICATION, - /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that system has been closed. + /// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that system has been closed. ID_CONNECTION_LOST, /// RakPeer - We preset an RSA public key which does not match what the system we connected to is using. ID_RSA_PUBLIC_KEY_MISMATCH, @@ -100,11 +107,11 @@ enum ID_REMOTE_DISCONNECTION_NOTIFICATION, /// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has been forcefully dropped. Packet::systemAddress is modified to reflect the systemAddress of this client. ID_REMOTE_CONNECTION_LOST, - /// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has connected. Packet::systemAddress is modified to reflect the systemAddress of this client. + /// ConnectionGraph plugin - In a client/server environment, a client other than ourselves has connected. Packet::systemAddress is modified to reflect the systemAddress of the client that is not connected directly to us. The packet encoding is SystemAddress 1, ConnectionGraphGroupID 1, SystemAddress 2, ConnectionGraphGroupID 2 ID_REMOTE_NEW_INCOMING_CONNECTION, - // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), partTotal (unsigned int), partLength (unsigned int), first part data (length <= MAX_MTU_SIZE) + // RakPeer - Downloading a large message. Format is ID_DOWNLOAD_PROGRESS (MessageID), partCount (unsigned int), partTotal (unsigned int), partLength (unsigned int), first part data (length <= MAX_MTU_SIZE). See the three parameters partCount, partTotal and partLength in OnFileProgress in FileListTransferCBInterface.h ID_DOWNLOAD_PROGRESS, - + /// FileListTransfer plugin - Setup data ID_FILE_LIST_TRANSFER_HEADER, /// FileListTransfer plugin - A file @@ -124,6 +131,8 @@ enum ID_REPLICA_MANAGER_SCOPE_CHANGE, /// ReplicaManager plugin - Serialized data of an object ID_REPLICA_MANAGER_SERIALIZE, + /// ReplicaManager plugin - New connection, about to send all world objects + ID_REPLICA_MANAGER_DOWNLOAD_STARTED, /// ReplicaManager plugin - Finished downloading all serialized objects ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE, @@ -180,6 +189,10 @@ enum ID_NAT_CONNECT_AT_TIME, /// NATPunchthrough plugin - Internal message to send a message (to punch through the nat) at a certain time ID_NAT_SEND_OFFLINE_MESSAGE_AT_TIME, + /// NATPunchthrough plugin - The facilitator is already attempting this connection + ID_NAT_IN_PROGRESS, + /// NATPunchthrough plugin - Another system tried to connect to us, and failed. See guid and system address field in the Packet structure + ID_NAT_REMOTE_CONNECTION_ATTEMPT_FAILED, /// LightweightDatabase plugin - Query ID_DATABASE_QUERY_REQUEST, @@ -202,11 +215,56 @@ enum ID_READY_EVENT_ALL_SET, /// ReadyEvent plugin - Request of ready event state - used for pulling data when newly connecting ID_READY_EVENT_QUERY, - + + /// Lobby packets. Second byte indicates type. + ID_LOBBY_GENERAL, + + /// Auto RPC procedure call + ID_AUTO_RPC_CALL, + + /// Auto RPC functionName to index mapping + ID_AUTO_RPC_REMOTE_INDEX, + + /// Auto RPC functionName to index mapping, lookup failed. Will try to auto recover + ID_AUTO_RPC_UNKNOWN_REMOTE_INDEX, + + /// Auto RPC error code + /// See AutoRPC.h for codes, stored in packet->data[1] + ID_RPC_REMOTE_ERROR, + + /// FileListTransfer transferring large files in chunks that are read only when needed, to save memory + ID_FILE_LIST_REFERENCE_PUSH, + + /// Force the ready event to all set + ID_READY_EVENT_FORCE_ALL_SET, + + /// Rooms function + ID_ROOMS_EXECUTE_FUNC, + ID_ROOMS_LOGON_STATUS, + ID_ROOMS_HANDLE_CHANGE, + + /// Lobby2 message + ID_LOBBY2_SEND_MESSAGE, + ID_LOBBY2_SERVER_ERROR, + + // RAKNET_PROTOCOL_VERSION in RakNetVersion.h does not match on the remote system what we have on our system + // This means the two systems cannot communicate. + // The 2nd byte of the message contains the value of RAKNET_PROTOCOL_VERSION for the remote system + ID_INCOMPATIBLE_PROTOCOL_VERSION, + + /// FCMHost plugin telling another system we are adding them as a participant + ID_FCM_HOST_ADD_PARTICIPANT_REQUEST, + /// FCMHost plugin notifying the user that the host has changed. 2nd byte is FCMHostGroupID + ID_FCM_HOST_CHANGED, + // FCMHost plugin sent us a new list + ID_FCM_HOST_LIST_UPDATE, + // For the user to use. Start your first enumeration at this value. ID_USER_PACKET_ENUM, //------------------------------------------------------------------------------------------------------------- }; +#endif // RAKNET_USE_CUSTOM_PACKET_IDS + #endif diff --git a/Multiplayer/raknet/NatPunchthrough.h b/Multiplayer/raknet/NatPunchthrough.h new file mode 100644 index 000000000..711d8c3ae --- /dev/null +++ b/Multiplayer/raknet/NatPunchthrough.h @@ -0,0 +1,193 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __NAT_PUNCHTHROUGH_H +#define __NAT_PUNCHTHROUGH_H + +#include "RakNetTypes.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 + +/// \ingroup NAT_PUNCHTHROUGH_GROUP +/// \brief Prints logging output for NatPunchthrough +class RAK_DLL_EXPORT NatPunchthroughLogger +{ +public: + NatPunchthroughLogger() {} + virtual ~NatPunchthroughLogger() {} + virtual void OnMessage(const char *str); +}; + +/// \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 +/// +/// Rebinding instances of RakPeer: +/// If after connecting two instances of RakPeer, you want to use the now open ports on the router with another instance of RakPeer, follow these steps +/// A. Connect the two systems by NAT punchthrough. Store the IP address of the system you connected to. You can get this with SystemAddress::ToString(false) +/// B. Once you get ID_CONNECTION_REQUEST_ACCEPTED or ID_NEW_INCOMING_CONNECTION, call RakPeer::Shutdown(0). This will stop RakNet without telling the other system. +/// C. Wait about double the ping time to make sure the other system had the opportunity to call RakPeer::Shutdown(0). +/// D. Recreate on the receiver the instance of RakPeer, in the new application. If you originally used port 1234, call Startup() again with that port. If you used port 0, you will need to know what port the socket was bound on. Use GetInternalID(UNASSIGNED_SYSTEM_ADDRESS).port anytime after calling Startup() +/// E. Recreate on the sender the instance of RakPeer in the new application, using the same port as originally used in the last instance of RakPeer on the sender. +/// F. Connect the sender to the receiver, using the port returned by NatPunchthrough::GetLastPortUsedToConnect() +/// +/// \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 + virtual ~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] destination 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* destination, unsigned short remotePort, const char *passwordData, int passwordDataLength, SystemAddress facilitator); + + /// Same as above, but takes a SystemAddress for a host + /// \param[in] destination 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(SystemAddress destination, const char *passwordData, int passwordDataLength, SystemAddress facilitator); + + /// Free internal memory. + void Clear(void); + + /// Sets the log output to print messages to + void SetLogger(NatPunchthroughLogger *l); + + /// Returns the last port we tried to connect on. + /// If at some point we get ID_CONNECTION_REQUEST_ACCEPTED, the remote router should be open on this port, + /// and should forward messages send on this port to the remotely bound port. + /// Returns 0 if none. + unsigned short GetLastPortUsedToConnect(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 OnShutdown(RakPeerInterface *peer); + /// \internal For plugin handling + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + + 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; + SystemAddress receiverPublic; + SystemAddress receiverPrivate[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + // Used to remove old connection Requests + RakNetTime timeoutTime; + RakNetGUID receiverGuid; + + // Used only by sender + SystemAddress facilitator; + char* passwordData; + int passwordDataLength; + SystemAddress advertisedAddress; + SystemAddress lastAddressAttempted; + + // Used only by facilitator + SystemAddress senderPublic; + SystemAddress senderPrivate[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + unsigned char pingCount; + RakNetGUID senderGuid; + + // Used by recipient + int recipientOfflineCount; + bool attemptedConnection; + + void GetAddressList(RakPeerInterface *rakPeer, DataStructures::List &fallbackAddresses, SystemAddress publicAddress, SystemAddress privateAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS], bool excludeConnected); + }; +protected: + void OnPunchthroughRequest(RakPeerInterface *peer, Packet *packet); + void OnNATAdvertiseRecipientPort(RakPeerInterface *peer, Packet *packet); + void OnConnectAtTime(RakPeerInterface *peer, Packet *packet); + void OnSendOfflineMessageAtTime(RakPeerInterface *peer, Packet *packet); + void RemoveRequestByFacilitator(SystemAddress systemAddress); + void LogOut(const char *l); + PluginReceiveResult OnConnectionAttemptFailed(Packet *packet); + void RemoveFromConnectionRequestList(SystemAddress systemAddress, RakNetGUID guid); + PluginReceiveResult OnNewIncomingConnection(Packet *packet); + + bool allowFacilitation; + RakPeerInterface *rakPeer; + DataStructures::List connectionRequestList; + NatPunchthroughLogger *log; + + // This is updated every time Connect() is called. You need to know it if you want to reconnect to that system without + // Running NAT punchthrough again + unsigned short lastPortUsedToConnect; +}; + +#endif diff --git a/Multiplayer/raknet/NativeTypes.h b/Multiplayer/raknet/NativeTypes.h new file mode 100644 index 000000000..6cb07e30f --- /dev/null +++ b/Multiplayer/raknet/NativeTypes.h @@ -0,0 +1,24 @@ +#ifndef __NATIVE_TYPES_H +#define __NATIVE_TYPES_H + + #if (defined(__GNUC__) || defined(__GCCXML__) || defined(__SNC__)) + #include + #endif + + #if !defined(_STDINT_H) && !defined(_SN_STDINT_H) + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + #if defined(_MSC_VER) && _MSC_VER < 1300 + typedef unsigned __int64 uint64_t; + typedef signed __int64 int64_t; + #else + typedef unsigned long long int uint64_t; + typedef signed long long int64_t; + #endif + #endif + +#endif diff --git a/Multiplayer/raknet/NetworkIDManager.h b/Multiplayer/raknet/NetworkIDManager.h new file mode 100644 index 000000000..be23d7439 --- /dev/null +++ b/Multiplayer/raknet/NetworkIDManager.h @@ -0,0 +1,128 @@ +/// \file +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __NETWORK_ID_MANAGER_H +#define __NETWORK_ID_MANAGER_H + +#include "DS_BinarySearchTree.h" +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" +#include "NetworkIDObject.h" + +/// \internal +/// \brief A node in the AVL tree that holds the mapping between NetworkID and pointers. +struct RAK_DLL_EXPORT NetworkIDNode +{ + NetworkID networkID; + NetworkIDObject *object; + NetworkIDNode(); + NetworkIDNode( NetworkID _networkID, NetworkIDObject *_object ); + bool operator==( const NetworkIDNode& right ) const; + bool operator > ( const NetworkIDNode& right ) const; + bool operator < ( const NetworkIDNode& right ) const; +}; + +/// This class is simply used to generate a unique number for a group of instances of NetworkIDObject +/// An instance of this class is required to use the ObjectID to pointer lookup system +/// You should have one instance of this class per game instance. +/// Call SetIsNetworkIDAuthority before using any functions of this class, or of NetworkIDObject +class RAK_DLL_EXPORT NetworkIDManager +{ +public: + NetworkIDManager(void); + virtual ~NetworkIDManager(void); + + /// 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 environment, the system that creates unique IDs would be the server. + /// If you are using peer to peer or other situations where you don't have a single system to assign ids, + /// set this to true, and be sure NETWORK_ID_SUPPORTS_PEER_TO_PEER is defined in RakNetDefines.h + void SetIsNetworkIDAuthority(bool isAuthority); + + /// \return Returns what was passed to SetIsNetworkIDAuthority() + bool IsNetworkIDAuthority(void) const; + + /// \Depreciated. Use SetGuid and GetGuid instead + /// 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::GetExternalSystemAddress) one time, the first time you make a connection. + /// \pre You must first call SetNetworkIDManager before using this function + /// \param[in] systemAddress Your external systemAddress + void SetExternalSystemAddress(SystemAddress systemAddress); + SystemAddress GetExternalSystemAddress(void); + + /// 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::GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS) one time, the first time you make a connection. + /// \pre You must first call SetNetworkIDManager before using this function + void SetGuid(RakNetGUID g); + RakNetGUID GetGuid(void); + + /// 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 + unsigned short GetSharedNetworkID( 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 NetworkIDObject reached. + void SetSharedNetworkID( unsigned short i ); + + /// If you use a parent, returns this instance rather than the parent object. + /// \pre You must first call SetNetworkIDManager before using this function + NetworkIDObject* GET_BASE_OBJECT_FROM_ID( NetworkID x ); + + /// Returns the parent object, or this instance if you don't use a parent. + /// \depreciated, use the template form. This form requires that NetworkIDObject is the basemost derived class + /// \pre You must first call SetNetworkIDManager before using this function + void* GET_OBJECT_FROM_ID( NetworkID x ); + + /// Returns the parent object, or this instance if you don't use a parent. + /// Supports NetworkIDObject anywhere in the inheritance hierarchy + /// \pre You must first call SetNetworkIDManager before using this function + template + returnType GET_OBJECT_FROM_ID(NetworkID x) { + NetworkIDObject *nio = GET_BASE_OBJECT_FROM_ID(x); + if (nio==0) + return 0; + if (nio->GetParent()) + return (returnType) nio->GetParent(); + return (returnType) nio; + } + +protected: + RakNetGUID guid; + // Depreciated - use guid instead. This has the problem that it can be different between the LAN and the internet, or duplicated on different LANs + SystemAddress externalSystemAddress; + unsigned short sharedNetworkID; + bool isNetworkIDAuthority; + bool calledSetIsNetworkIDAuthority; + friend class NetworkIDObject; + + +#if ! defined(NETWORK_ID_USE_PTR_TABLE) || defined(NETWORK_ID_USE_HASH) + /// This AVL tree holds the pointer to NetworkID mappings + DataStructures::AVLBalancedBinarySearchTree IDTree; +#else + NetworkIDObject **IDArray; +#endif +}; + +#endif diff --git a/Multiplayer/raknet/NetworkIDObject.h b/Multiplayer/raknet/NetworkIDObject.h new file mode 100644 index 000000000..117c6be55 --- /dev/null +++ b/Multiplayer/raknet/NetworkIDObject.h @@ -0,0 +1,98 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#if !defined(__NETWORK_ID_GENERATOR) +#define __NETWORK_ID_GENERATOR + +#include "RakNetTypes.h" +#include "RakMemoryOverride.h" +#include "Export.h" + +class NetworkIDManager; + +/// \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 NetworkIDObject +{ +public: + /// Constructor. NetworkIDs, if IsNetworkIDAuthority() is true, are created here. + NetworkIDObject(); + + /// Destructor. Used NetworkIDs, if any, are freed here. + virtual ~NetworkIDObject(); + + /// Sets the manager class from which to request unique network IDs + /// Unlike previous versions, the NetworkIDObject relies on a manager class to provide IDs, rather than using statics, + /// So you can have more than one set of IDs on the same system. + virtual void SetNetworkIDManager( NetworkIDManager *manager); + + /// Returns what was passed to SetNetworkIDManager + virtual NetworkIDManager * GetNetworkIDManager( void ); + + /// Returns the NetworkID that you can use to refer to this object over the network. + /// \pre You must first call SetNetworkIDManager before using this function + /// \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 NetworkIDObject, 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; + + /// 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; + + /// Used so I can compare pointers in the ReplicaManager + unsigned int GetAllocationNumber(void) const; + +protected: + /// The network ID of this object + NetworkID networkID; + + /// The parent set by SetParent() + void *parent; + + /// Used so I can compare pointers in the ReplicaManager + static unsigned int nextAllocationNumber; + unsigned int allocationNumber; + + /// 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; + + NetworkIDManager *networkIDManager; +}; + +#endif diff --git a/Multiplayer/raknet/PacketConsoleLogger.h b/Multiplayer/raknet/PacketConsoleLogger.h new file mode 100644 index 000000000..00222c453 --- /dev/null +++ b/Multiplayer/raknet/PacketConsoleLogger.h @@ -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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __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 RAK_DLL_EXPORT 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 diff --git a/Multiplayer/raknet/PacketFileLogger.h b/Multiplayer/raknet/PacketFileLogger.h new file mode 100644 index 000000000..c23e881c4 --- /dev/null +++ b/Multiplayer/raknet/PacketFileLogger.h @@ -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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __PACKET_FILE_LOGGER_H_ +#define __PACKET_FILE_LOGGER_H_ + +#include "PacketLogger.h" +#include + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Packetlogger that outputs to a file +class RAK_DLL_EXPORT PacketFileLogger : public PacketLogger +{ +public: + PacketFileLogger(); + virtual ~PacketFileLogger(); + void StartLog(const char *filenamePrefix); + virtual void WriteLog(const char *str); +protected: + FILE *packetLogFile; +}; + +#endif diff --git a/Multiplayer/raknet/PacketLogger.h b/Multiplayer/raknet/PacketLogger.h new file mode 100644 index 000000000..5488d360e --- /dev/null +++ b/Multiplayer/raknet/PacketLogger.h @@ -0,0 +1,86 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __PACKET_LOGGER_H +#define __PACKET_LOGGER_H + +class RakPeerInterface; +#include "RakNetTypes.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); + + virtual void Update(RakPeerInterface *peer); + + // Translate the supplied parameters into an output line - overloaded version that takes a MessageIdentifier + // and translates it into a string (numeric or textual representation based on printId); this calls the + // second version which takes a const char* argument for the messageIdentifier + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int packet, unsigned int frame + , unsigned char messageIdentifier, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + virtual void FormatLine(char* into, const char* dir, const char* type, unsigned int packet, unsigned int frame + , const char* idToPrint, const BitSize_t bitLen, unsigned long long time, const SystemAddress& local, const SystemAddress& remote, + unsigned int splitPacketId, unsigned int splitPacketIndex, unsigned int splitPacketCount, unsigned int orderingIndex); + + /// 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 BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + virtual void OnInternalPacket(InternalPacket *internalPacket, unsigned frameNumber, SystemAddress remoteSystemAddress, 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); + + /// Prepend this string to output logs. + virtual void SetPrefix(const char *_prefix); + + /// Append this string to output logs. (newline is useful here) + virtual void SetSuffix(const char *_suffix); + static const char* BaseIDTOString(unsigned char Id); +protected: + const char* IDTOString(unsigned char Id); + virtual void AddToLog(const char *str); + // Users should override this + virtual const char* UserIDTOString(unsigned char Id); + + RakPeerInterface *rakPeer; + bool printId, printAcks; + char prefix[256]; + char suffix[256]; +}; + +#endif diff --git a/Multiplayer/raknet/PacketPool.h b/Multiplayer/raknet/PacketPool.h new file mode 100644 index 000000000..b534cac46 --- /dev/null +++ b/Multiplayer/raknet/PacketPool.h @@ -0,0 +1 @@ +// REMOVEME \ No newline at end of file diff --git a/Multiplayer/raknet/PacketPriority.h b/Multiplayer/raknet/PacketPriority.h index 9ff922322..440f79c83 100644 --- a/Multiplayer/raknet/PacketPriority.h +++ b/Multiplayer/raknet/PacketPriority.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -21,10 +21,19 @@ /// These enumerations are used to describe when packets are delivered. enum PacketPriority { - SYSTEM_PRIORITY, /// \internal Used by RakNet to send above-high priority messages. - HIGH_PRIORITY, /// High priority messages are send before medium priority messages. - MEDIUM_PRIORITY, /// Medium priority messages are send before low priority messages. - LOW_PRIORITY, /// Low priority messages are only sent when no other messages are waiting. + /// \internal Used by RakNet to send above-high priority messages. + SYSTEM_PRIORITY, + + /// High priority messages are send before medium priority messages. + HIGH_PRIORITY, + + /// Medium priority messages are send before low priority messages. + MEDIUM_PRIORITY, + + /// Low priority messages are only sent when no other messages are waiting. + LOW_PRIORITY, + + /// \internal NUMBER_OF_PRIORITIES }; @@ -32,11 +41,23 @@ 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, /// 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. - RELIABLE_SEQUENCED /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. + /// 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, + + /// 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. + UNRELIABLE_SEQUENCED, + + /// The message is sent reliably, but not necessarily in any order. Same overhead as UNRELIABLE. + RELIABLE, + + /// 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. + RELIABLE_ORDERED, + + /// This message is reliable and will arrive in the sequence you sent it. Out or order messages will be dropped. Same overhead as UNRELIABLE_SEQUENCED. + RELIABLE_SEQUENCED, + + /// \internal + NUMBER_OF_RELIABILITIES }; #endif diff --git a/Multiplayer/raknet/Platform.h b/Multiplayer/raknet/Platform.h new file mode 100644 index 000000000..3fcbc6eb4 --- /dev/null +++ b/Multiplayer/raknet/Platform.h @@ -0,0 +1,262 @@ +#ifndef PLATFORM_HPP +#define PLATFORM_HPP + + +//// Platform tweaks //// + +#include "RakNetTypes.h" + +#ifdef _WIN32_WCE //makslane +# define _byteswap_ushort(s) ((s >> 8) | (s << 8)) +# define _byteswap_ulong(s) (byteswap_ushort(s&0xffff)<<16) | (byteswap_ushort(s>>16)) +# define byteswap_ushort _byteswap_ushort +# define byteswap_ulong _byteswap_ulong +#endif + +#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) +# if defined(__sparc) || defined(__sparc__) || defined(__powerpc__) || \ + defined(__ppc__) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || \ + defined(_M_PPC) || defined(_M_MPPC) || defined(_M_MRX000) || \ + defined(__POWERPC) || defined(m68k) || defined(powerpc) || \ + defined(sel) || defined(pyr) || defined(mc68000) || defined(is68k) || \ + defined(tahoe) || defined(ibm032) || defined(ibm370) || defined(MIPSEB) || \ + defined(__convex__) || defined(DGUX) || defined(hppa) || defined(apollo) || \ + defined(_CRAY) || defined(__hp9000) || defined(__hp9000s300) || defined(_AIX) || \ + defined(__AIX) || defined(__pyr__) || defined(hp9000s700) || defined(_IBMR2) +# define BIG_ENDIAN +# elif defined(__i386__) || defined(i386) || defined(intel) || defined(_M_IX86) || \ + defined(__alpha__) || defined(__alpha) || defined(__ia64) || defined(__ia64__) || \ + defined(_M_ALPHA) || defined(ns32000) || defined(__ns32000__) || defined(sequent) || \ + defined(MIPSEL) || defined(_MIPSEL) || defined(sun386) || defined(__sun386__) || \ + defined(x86_64) || defined(__x86_64) +# define LITTLE_ENDIAN +# else +# error "Add your platform to the list" +# endif +#endif + + +//// Compiler tweaks //// + +// Structure packing syntax +#if defined(__GNUC__) +#ifndef PACKED +# define PACKED __attribute__((__packed__)) +#endif +# define ASSEMBLY_ATT_SYNTAX +# define ASSEMBLY_BLOCK asm +#elif defined(_WIN32) +# define PACKED +# define PRAGMA_PACK /* pragma push/pop */ +#ifndef DEBUG +# define DEBUG _DEBUG +#endif +# if !defined(_M_X64) +# define ASSEMBLY_INTEL_SYNTAX +# define ASSEMBLY_BLOCK __asm +# endif +#elif defined(__BORLANDC__) // Borland +# define ASSEMBLY_INTEL_SYNTAX +# define ASSEMBLY_BLOCK _asm +#else +# error "Fix code for your compiler's packing syntax" +#endif + +// Stringize +#define STRINGIZE(X) DO_STRINGIZE(X) +#define DO_STRINGIZE(X) #X + + +//// Elementary types //// + +struct u128 { uint32_t u[4]; }; + +union Float32 { + float f; + uint32_t i; + + Float32(float n) { f = n; } + Float32(uint32_t n) { i = n; } +}; + + +//// Coordinate system //// + +// Order of vertices in any quad +enum QuadCoords +{ + QUAD_UL, // upper left (0,0) + QUAD_LL, // lower left (0,1) + QUAD_LR, // lower right (1,1) + QUAD_UR, // upper right (1,0) +}; + + +//// Rotation macros //// + +#ifndef ROL8 +#define ROL8(n, r) ( ((uint8_t)(n) << (r)) | ((uint8_t)(n) >> ( 8 - (r))) ) /* only works for uint8_t */ +#endif +#ifndef ROR8 +#define ROR8(n, r) ( ((uint8_t)(n) >> (r)) | ((uint8_t)(n) << ( 8 - (r))) ) /* only works for uint8_t */ +#endif +#ifndef ROL16 +#define ROL16(n, r) ( ((uint16_t)(n) << (r)) | ((uint16_t)(n) >> (16 - (r))) ) /* only works for uint16_t */ +#endif +#ifndef ROR16 +#define ROR16(n, r) ( ((uint16_t)(n) >> (r)) | ((uint16_t)(n) << (16 - (r))) ) /* only works for uint16_t */ +#endif +#ifndef ROL32 +#define ROL32(n, r) ( ((uint32_t)(n) << (r)) | ((uint32_t)(n) >> (32 - (r))) ) /* only works for uint32_t */ +#endif +#ifndef ROR32 +#define ROR32(n, r) ( ((uint32_t)(n) >> (r)) | ((uint32_t)(n) << (32 - (r))) ) /* only works for uint32_t */ +#endif +#ifndef ROL64 +#define ROL64(n, r) ( ((uint64_t)(n) << (r)) | ((uint64_t)(n) >> (64 - (r))) ) /* only works for uint64_t */ +#endif +#ifndef ROR64 +#define ROR64(n, r) ( ((uint64_t)(n) >> (r)) | ((uint64_t)(n) << (64 - (r))) ) /* only works for uint64_t */ +#endif + +//// Byte-order swapping //// + +#ifndef BOSWAP16 +#define BOSWAP16(n) ROL16(n, 8) +#endif +#ifndef BOSWAP32 +#define BOSWAP32(n) ( (ROL32(n, 8) & 0x00ff00ff) | (ROL32(n, 24) & 0xff00ff00) ) +#endif +#ifndef BOSWAP64 +#define BOSWAP64(n) ( ((uint64_t)BOSWAP32((uint32_t)n) << 32) | BOSWAP32((uint32_t)(n >> 32)) ) +#endif + +//// Miscellaneous bitwise macros //// + +#define BITCLRHI8(reg, count) ((uint8_t)((uint8_t)(reg) << (count)) >> (count)) /* sets to zero a number of high bits in a byte */ +#define BITCLRLO8(reg, count) ((uint8_t)((uint8_t)(reg) >> (count)) << (count)) /* sets to zero a number of low bits in a byte */ +#define BITCLRHI16(reg, count) ((uint16_t)((uint16_t)(reg) << (count)) >> (count)) /* sets to zero a number of high bits in a 16-bit word */ +#define BITCLRLO16(reg, count) ((uint16_t)((uint16_t)(reg) >> (count)) << (count)) /* sets to zero a number of low bits in a 16-bit word */ +#define BITCLRHI32(reg, count) ((uint32_t)((uint32_t)(reg) << (count)) >> (count)) /* sets to zero a number of high bits in a 32-bit word */ +#define BITCLRLO32(reg, count) ((uint32_t)((uint32_t)(reg) >> (count)) << (count)) /* sets to zero a number of low bits in a 32-bit word */ + + +//// Integer macros //// + +#define AT_LEAST_2_BITS(n) ( (n) & ((n) - 1) ) +#define LEAST_SIGNIFICANT_BIT(n) ( (n) & -(n) ) /* 0 -> 0 */ +#define IS_POWER_OF_2(n) ( n && !AT_LEAST_2_BITS(n) ) + +// Bump 'n' to the next unit of 'width' +// 0=CEIL_UNIT(0, 16), 1=CEIL_UNIT(1, 16), 1=CEIL_UNIT(16, 16), 2=CEIL_UNIT(17, 16) +#define CEIL_UNIT(n, width) ( ( (n) + (width) - 1 ) / (width) ) +// 0=CEIL(0, 16), 16=CEIL(1, 16), 16=CEIL(16, 16), 32=CEIL(17, 16) +#define CEIL(n, width) ( CEIL_UNIT(n, width) * (width) ) + + +//// String and buffer macros //// + +// Same as strncpy() in all ways except that the result is guaranteed to +// be a nul-terminated C string +#ifndef STRNCPY +# define STRNCPY(dest, src, size) { strncpy(dest, src, size); (dest)[(size)-1] = '\0'; } +#endif + +// Because memory clearing is a frequent operation +#define MEMCLR(dest, size) memset(dest, 0, size) + +// Works for arrays, also +#define OBJCLR(object) memset(&(object), 0, sizeof(object)) + + +//// Intrinsics //// + +#if defined(_MSC_VER) + +# include + +# pragma intrinsic(_lrotl) +# pragma intrinsic(_lrotr) +# pragma intrinsic(_byteswap_ushort) +# pragma intrinsic(_byteswap_ulong) + +# undef ROL32 +# undef ROR32 +# undef BOSWAP16 +# undef BOSWAP32 + +# define ROL32(n, r) _lrotl(n, r) +# define ROR32(n, r) _lrotr(n, r) +# define BOSWAP16(n) _byteswap_ushort(n) +# define BOSWAP32(n) _byteswap_ulong(n) + +#endif + + +// getLE() converts from little-endian word to native byte-order word +// getBE() converts from big-endian word to native byte-order word + +#if defined(LITTLE_ENDIAN) + +# define swapLE(n) +# define getLE(n) (n) +# define getLE16(n) (n) +# define getLE32(n) (n) +# define getLE64(n) (n) + + inline void swapBE(uint16_t &n) { n = BOSWAP16(n); } + inline void swapBE(uint32_t &n) { n = BOSWAP32(n); } + inline void swapBE(uint64_t &n) { n = BOSWAP64(n); } + inline uint16_t getBE(uint16_t n) { return BOSWAP16(n); } + inline uint32_t getBE(uint32_t n) { return BOSWAP32(n); } + inline uint64_t getBE(uint64_t n) { return BOSWAP64(n); } + inline uint16_t getBE16(uint16_t n) { return BOSWAP16(n); } + inline uint32_t getBE32(uint32_t n) { return BOSWAP32(n); } + inline uint64_t getBE64(uint64_t n) { return BOSWAP64(n); } + inline void swapBE(int16_t &n) { n = BOSWAP16((uint16_t)n); } + inline void swapBE(int32_t &n) { n = BOSWAP32((uint32_t)n); } + inline void swapBE(int64_t &n) { n = BOSWAP64((uint64_t)n); } + inline int16_t getBE(int16_t n) { return BOSWAP16((uint16_t)n); } + inline int32_t getBE(int32_t n) { return BOSWAP32((uint32_t)n); } + inline int64_t getBE(int64_t n) { return BOSWAP64((uint64_t)n); } + + inline float getBE(float n) { + Float32 c = n; + c.i = BOSWAP32(c.i); + return c.f; + } + +#else + +# define swapBE(n) +# define getBE(n) (n) +# define getBE16(n) (n) +# define getBE32(n) (n) +# define getBE64(n) (n) + + inline void swapLE(uint16_t &n) { n = BOSWAP16(n); } + inline void swapLE(uint32_t &n) { n = BOSWAP32(n); } + inline void swapLE(uint64_t &n) { n = BOSWAP64(n); } + inline uint16_t getLE(uint16_t n) { return BOSWAP16(n); } + inline uint32_t getLE(uint32_t n) { return BOSWAP32(n); } + inline uint64_t getLE(uint64_t n) { return BOSWAP64(n); } + inline uint16_t getLE16(uint16_t n) { return BOSWAP16(n); } + inline uint32_t getLE32(uint32_t n) { return BOSWAP32(n); } + inline uint64_t getLE32(uint64_t n) { return BOSWAP64(n); } + inline void swapLE(int16_t &n) { n = BOSWAP16((uint16_t)n); } + inline void swapLE(int32_t &n) { n = BOSWAP32((uint32_t)n); } + inline void swapLE(int64_t &n) { n = BOSWAP64((uint64_t)n); } + inline int16_t getLE(int16_t n) { return BOSWAP16((uint16_t)n); } + inline int32_t getLE(int32_t n) { return BOSWAP32((uint32_t)n); } + inline int64_t getLE(int64_t n) { return BOSWAP64((uint64_t)n); } + + inline float getLE(float n) { + Float32 c = n; + c.i = BOSWAP32(c.i); + return c.f; + } + +#endif + + +#endif // include guard diff --git a/Multiplayer/raknet/PluginInterface.h b/Multiplayer/raknet/PluginInterface.h new file mode 100644 index 000000000..5546f95cb --- /dev/null +++ b/Multiplayer/raknet/PluginInterface.h @@ -0,0 +1,111 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __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 "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.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: + PluginInterface(); + virtual ~PluginInterface(); + + /// 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 OnStartup(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 OnShutdown(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] systemAddress The system whose connection was closed + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + + /// 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] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketSend(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + + /// 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] remoteSystemAddress Which system this message is being sent to + virtual void OnDirectSocketReceive(const char *data, const BitSize_t bitsUsed, SystemAddress remoteSystemAddress); + + /// 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] remoteSystemAddress 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, SystemAddress remoteSystemAddress, RakNetTime time, bool isSend); +}; + +#endif + diff --git a/Multiplayer/raknet/RPCMap.h b/Multiplayer/raknet/RPCMap.h new file mode 100644 index 000000000..d708664d7 --- /dev/null +++ b/Multiplayer/raknet/RPCMap.h @@ -0,0 +1,49 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RPC_MAP +#define __RPC_MAP + +#include "RakMemoryOverride.h" +#include "RPCNode.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "Export.h" + +/// \ingroup RAKNET_RPC +/// \internal +/// \brief A container class for a list of RPCNodes +struct RAK_DLL_EXPORT RPCMap +{ +public: + RPCMap(); + ~RPCMap(); + void Clear(void); + RPCNode *GetNodeFromIndex(RPCIndex index); + RPCNode *GetNodeFromFunctionName(const char *uniqueIdentifier); + RPCIndex GetIndexFromFunctionName(const char *uniqueIdentifier); + void AddIdentifierWithFunction(const char *uniqueIdentifier, void *functionPointer, bool isPointerToMember); + void AddIdentifierAtIndex(const char *uniqueIdentifier, RPCIndex insertionIndex); + void RemoveNode(const char *uniqueIdentifier); +protected: + DataStructures::List rpcSet; +}; + +#endif + diff --git a/Multiplayer/raknet/RPCNode.h b/Multiplayer/raknet/RPCNode.h new file mode 100644 index 000000000..1f0f381c9 --- /dev/null +++ b/Multiplayer/raknet/RPCNode.h @@ -0,0 +1,61 @@ +/// \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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RPC_NODE +#define __RPC_NODE + +#include "RakNetTypes.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 +/// \internal +/// +/// \brief Map registered procedure inside of a peer. +/// +struct RAK_DLL_EXPORT RPCNode +{ + + /// String identifier of the RPC + char *uniqueIdentifier; + + /// Force casting of member functions to void * + union + { + void ( *staticFunctionPointer ) ( RPCParameters *rpcParms ); + #if (defined(__GNUC__) || defined(__GCCXML__)) + void (*memberFunctionPointer)(void* _this, RPCParameters *rpcParms); + #else + void (__cdecl *memberFunctionPointer)(void* _this, RPCParameters *rpcParms); + #endif + + void *functionPointer; + }; + + /// Is this a member function pointer? True if so. If false it's a regular C function. + bool isPointerToMember; +}; + +#endif + diff --git a/Multiplayer/raknet/RSACrypt.h b/Multiplayer/raknet/RSACrypt.h new file mode 100644 index 000000000..22c1bef24 --- /dev/null +++ b/Multiplayer/raknet/RSACrypt.h @@ -0,0 +1,47 @@ +#ifndef RSA_CRYPT_HPP +#define RSA_CRYPT_HPP + +#include "Platform.h" +#include "Export.h" + +bool primeTest(const uint32_t *n, int limbs, uint32_t k); +void generateStrongPseudoPrime(uint32_t *n, int limbs); + +class RAK_DLL_EXPORT RSACrypt +{ + uint32_t *p, p_inv, *q, q_inv, *pinvq, factor_limbs; + uint32_t *d, e; + uint32_t *modulus, mod_inv, mod_limbs; + + void cleanup(); + + bool generateExponents(const uint32_t *p, const uint32_t *q, int limbs, uint32_t &e, uint32_t *d); + +public: + RSACrypt(); + ~RSACrypt(); + +public: + bool setPrivateKey(const uint32_t *p, const uint32_t *q, int halfFactorLimbs); + bool setPublicKey(const uint32_t *modulus, int mod_limbs, uint32_t e); + +public: + // Bitsize is limbs * 32 + // Private key size is limbs/2 words + bool generatePrivateKey(uint32_t limbs); // limbs must be a multiple of 2 + +public: + uint32_t getFactorLimbs(); + void getPrivateP(uint32_t *p); // p buffer has factor_limbs + void getPrivateQ(uint32_t *q); // q buffer has factor_limbs + + uint32_t getModLimbs(); + void getPublicModulus(uint32_t *modulus); // modulus buffer has mod_limbs + uint32_t getPublicExponent(); + +public: + bool encrypt(uint32_t *ct, const uint32_t *pt); // pt limbs = mod_limbs + bool decrypt(uint32_t *pt, const uint32_t *ct); // ct limbs = mod_limbs +}; + +#endif // RSA_CRYPT_HPP diff --git a/Multiplayer/raknet/RakAssert.h b/Multiplayer/raknet/RakAssert.h new file mode 100644 index 000000000..21676d2cb --- /dev/null +++ b/Multiplayer/raknet/RakAssert.h @@ -0,0 +1,16 @@ +#ifndef __RAK_ASSERT_H +#define __RAK_ASSERT_H + +/* +// So stupid Linux doesn't assert in release +#if defined(_DEBUG) && !defined(_XBOX) +#define RakAssert(x) assert(x); +#else +*/ +#define RakAssert(x) +/* +#endif + +*/ + +#endif diff --git a/Multiplayer/raknet/RakMemoryOverride.h b/Multiplayer/raknet/RakMemoryOverride.h new file mode 100644 index 000000000..06bb15ba0 --- /dev/null +++ b/Multiplayer/raknet/RakMemoryOverride.h @@ -0,0 +1,136 @@ +/// \file +/// \brief If _USE_RAK_MEMORY_OVERRIDE is defined, memory allocations go through rakMalloc, rakRealloc, and rakFree +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAK_MEMORY_H +#define __RAK_MEMORY_H + +#include "Export.h" +#include "RakNetDefines.h" +#include + +#if defined(_XBOX) || defined(X360) +#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +// Causes linker errors +// #include +typedef unsigned int size_t; +#elif defined ( __APPLE__ ) || defined ( __APPLE_CC__ ) +#include +#elif defined(_WIN32) +#include +#else +#if !defined ( __FreeBSD__ ) +#include +#endif +#include +#endif + +#if defined(_USE_RAK_MEMORY_OVERRIDE) + #if defined(new) + #pragma push_macro("new") + #undef new + #define RMO_NEW_UNDEF + #endif +#endif + +// These pointers are statically and globally defined in RakMemoryOverride.cpp +// Change them to point to your own allocators if you want. +extern "C" { +extern void* (*rakMalloc) (size_t size); +extern void* (*rakRealloc) (void *p, size_t size); +extern void (*rakFree) (void *p); +extern void (*notifyOutOfMemory) (const char *file, const long line); +} + +namespace RakNet +{ + + template + Type* OP_NEW(void) + { +#if defined(_USE_RAK_MEMORY_OVERRIDE) + char *buffer = (char *) rakMalloc(sizeof(Type)); + Type *t = new (buffer) Type; + return t; +#else + return new Type; +#endif + } + + template + Type* OP_NEW_ARRAY(const int count) + { +#if defined(_USE_RAK_MEMORY_OVERRIDE) + Type *t; + char *buffer = (char *) rakMalloc(sizeof(int)+sizeof(Type)*count); + ((int*)buffer)[0]=count; + for (int i=0; i + void OP_DELETE(Type *buff) + { +#if defined(_USE_RAK_MEMORY_OVERRIDE) + if (buff==0) return; + buff->~Type(); + rakFree((char*)buff); +#else + delete buff; +#endif + + } + + template + void OP_DELETE_ARRAY(Type *buff) + { +#if defined(_USE_RAK_MEMORY_OVERRIDE) + if (buff==0) return; + int count = ((int*)((char*)buff-sizeof(int)))[0]; + Type *t; + for (int i=0; i~Type(); + } + rakFree((char*)buff-sizeof(int)); +#else + delete [] buff; +#endif + + } + +void* _RakMalloc (size_t size); +void* _RakRealloc (void *p, size_t size); +void _RakFree (void *p); + +} + +#if defined(_USE_RAK_MEMORY_OVERRIDE) + #if defined(RMO_NEW_UNDEF) + #pragma pop_macro("new") + #undef RMO_NEW_UNDEF + #endif +#endif + +#endif diff --git a/Multiplayer/raknet/RakNetCommandParser.h b/Multiplayer/raknet/RakNetCommandParser.h new file mode 100644 index 000000000..f6d7d7b4f --- /dev/null +++ b/Multiplayer/raknet/RakNetCommandParser.h @@ -0,0 +1,60 @@ +/// \file +/// \brief Contains RakNetCommandParser , used to send commands to an instance of RakPeer +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAKNET_COMMAND_PARSER +#define __RAKNET_COMMAND_PARSER + +#include "CommandParserInterface.h" +#include "Export.h" +class RakPeerInterface; + +/// \brief This allows a console client to call most of the functions in RakPeer +class RAK_DLL_EXPORT RakNetCommandParser : public CommandParserInterface +{ +public: + RakNetCommandParser(); + ~RakNetCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char *GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, SystemAddress systemAddress); + + /// Records the instance of RakPeer to perform the desired commands on + /// \param[in] rakPeer The RakPeer instance, or a derived class (e.g. RakPeer or RakPeer) + void SetRakPeerInterface(RakPeerInterface *rakPeer); +protected: + + /// Which instance of RakPeer we are working on. Set from SetRakPeerInterface() + RakPeerInterface *peer; +}; + +#endif diff --git a/Multiplayer/raknet/RakNetDefines.h b/Multiplayer/raknet/RakNetDefines.h index 3de9af8e8..68d7d2a77 100644 --- a/Multiplayer/raknet/RakNetDefines.h +++ b/Multiplayer/raknet/RakNetDefines.h @@ -1,3 +1,6 @@ +#ifndef __RAKNET_DEFINES_H +#define __RAKNET_DEFINES_H + /// Define __GET_TIME_64BIT to have RakNetTime use a 64, rather than 32 bit value. A 32 bit value will overflow after about 5 weeks. /// However, this doubles the bandwidth use for sending times, so don't do it unless you have a reason to. /// Disabled by default. @@ -12,9 +15,9 @@ /// Define __BITSTREAM_NATIVE_END to NOT support endian swapping in the BitStream class. This is faster and is what you should use /// unless you actually plan to have different endianness systems connect to each other /// Enabled by default. -#define __BITSTREAM_NATIVE_END +// #define __BITSTREAM_NATIVE_END -#if defined(_CONSOLE_2) +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) #undef __BITSTREAM_NATIVE_END #endif @@ -24,7 +27,42 @@ // Use WaitForSingleObject instead of sleep. // Defining it plays nicer with other systems, and uses less CPU, but gives worse RakNet performance // Undefining it uses more CPU time, but is more responsive and faster. +#ifndef _WIN32_WCE #define USE_WAIT_FOR_MULTIPLE_EVENTS +#endif -// If you need it -#define RAKNET_VERSION "3.0 09/07/07" +/// Uncomment to use RakMemoryOverride for custom memory tracking +/// See RakMemoryOverride.h. +#define _USE_RAK_MEMORY_OVERRIDE + +/// If defined, RakNet will automatically try to determine available bandwidth and buffer accordingly (recommended) +/// If commented out, you will probably not be able to send large files and will get increased packetloss. However, responsiveness for the first 10 seconds or so will be improved. +#define _ENABLE_FLOW_CONTROL + +/// If defined, OpenSSL is enabled for the class TCPInterface +/// This is necessary to use the SendEmail class with Google POP servers +/// Note that OpenSSL carries its own license restrictions that you should be aware of. If you don't agree, don't enable this define +/// This also requires that you enable header search paths to DependentExtensions\openssl-0.9.8g +/// #define OPEN_SSL_CLIENT_SUPPORT + +/// Threshold at which to do a malloc / free rather than pushing data onto a fixed stack for the bitstream class +/// Arbitrary size, just picking something likely to be larger than most packets +#define BITSTREAM_STACK_ALLOCATION_SIZE 1024 + +// Redefine if you want to disable or change the target for debug RAKNET_DEBUG_PRINTF +#define RAKNET_DEBUG_PRINTF printf + +// 16 * 4 * 8 = 512 bit. Used for InitializeSecurity() +#define RAKNET_RSA_FACTOR_LIMBS 16 + +// Enable to support peer to peer with NetworkIDs. Disable to save memory if doing client/server only +#define NETWORK_ID_SUPPORTS_PEER_TO_PEER + +// O(1) instead of O(log2n) but takes more memory if less than 1/3 of the mappings are used. +// Only supported if NETWORK_ID_SUPPORTS_PEER_TO_PEER is commented out +// #define NETWORK_ID_USE_PTR_TABLE + +// Maximum number of local IP addresses supported +#define MAXIMUM_NUMBER_OF_INTERNAL_IDS 5 + +#endif diff --git a/Multiplayer/raknet/RakNetLibStatic.lib b/Multiplayer/raknet/RakNetLibStatic.lib index b61bfc971..979011a83 100644 Binary files a/Multiplayer/raknet/RakNetLibStatic.lib and b/Multiplayer/raknet/RakNetLibStatic.lib differ diff --git a/Multiplayer/raknet/RakNetLibStaticDebug.lib b/Multiplayer/raknet/RakNetLibStaticDebug.lib index dc3f09c6a..2efa71209 100644 Binary files a/Multiplayer/raknet/RakNetLibStaticDebug.lib and b/Multiplayer/raknet/RakNetLibStaticDebug.lib differ diff --git a/Multiplayer/raknet/RakNetStatistics.h b/Multiplayer/raknet/RakNetStatistics.h index f521e16d7..b9517a068 100644 --- a/Multiplayer/raknet/RakNetStatistics.h +++ b/Multiplayer/raknet/RakNetStatistics.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -33,28 +33,28 @@ struct RAK_DLL_EXPORT RakNetStatistics /// Number of messages sent (high, medium, low priority) unsigned messagesSent[ NUMBER_OF_PRIORITIES ]; /// Number of data bits used for user messages - unsigned messageDataBitsSent[ NUMBER_OF_PRIORITIES ]; + uint64_t messageDataBitsSent[ NUMBER_OF_PRIORITIES ]; /// Number of total bits used for user messages, including headers - unsigned messageTotalBitsSent[ NUMBER_OF_PRIORITIES ]; + uint64_t messageTotalBitsSent[ NUMBER_OF_PRIORITIES ]; - /// Number of packets sent containing only acknowledgements + /// Number of packets sent containing only acknowledgments unsigned packetsContainingOnlyAcknowlegements; - /// Number of acknowledgements sent + /// Number of acknowledgments sent unsigned acknowlegementsSent; - /// Number of acknowledgements waiting to be sent + /// Number of acknowledgments waiting to be sent unsigned acknowlegementsPending; - /// Number of acknowledgements bits sent - unsigned acknowlegementBitsSent; + /// Number of acknowledgments bits sent + uint64_t acknowlegementBitsSent; - /// Number of packets containing only acknowledgements and resends + /// Number of packets containing only acknowledgments and resends unsigned packetsContainingOnlyAcknowlegementsAndResends; /// Number of messages resent unsigned messageResends; /// Number of bits resent of actual data - unsigned messageDataBitsResent; + uint64_t messageDataBitsResent; /// Total number of bits resent, including headers - unsigned messagesTotalBitsResent; + uint64_t messagesTotalBitsResent; /// Number of messages waiting for ack (// TODO - rename this) unsigned messagesOnResendQueue; @@ -69,9 +69,9 @@ struct RAK_DLL_EXPORT RakNetStatistics unsigned packetsSent; /// Number of bits added by encryption - unsigned encryptionBitsSent; + uint64_t encryptionBitsSent; /// total bits sent - unsigned totalBitsSent; + uint64_t totalBitsSent; /// Number of sequenced messages arrived out of order unsigned sequencedMessagesOutOfOrder; @@ -88,9 +88,9 @@ struct RAK_DLL_EXPORT RakNetStatistics /// Packets with a bad CRC received unsigned packetsWithBadCRCReceived; /// Bits with a good CRC received - unsigned bitsReceived; + uint64_t bitsReceived; /// Bits with a bad CRC received - unsigned bitsWithBadCRCReceived; + uint64_t bitsWithBadCRCReceived; /// Number of acknowledgement messages received for packets we are resending unsigned acknowlegementsReceived; /// Number of acknowledgement messages received for packets we are not resending diff --git a/Multiplayer/raknet/RakNetTime.h b/Multiplayer/raknet/RakNetTime.h new file mode 100644 index 000000000..2a800fa47 --- /dev/null +++ b/Multiplayer/raknet/RakNetTime.h @@ -0,0 +1,30 @@ +#ifndef __RAKNET_TIME_H +#define __RAKNET_TIME_H + +// 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) +#ifdef __GET_TIME_64BIT +#if defined(_MSC_VER) && _MSC_VER < 1300 +typedef unsigned __int64 RakNetTime; +typedef unsigned __int64 RakNetTimeNS; +typedef unsigned __int64 RakNetTimeMS; +typedef unsigned __int64 RakNetTimeUS; +#else +typedef unsigned long long RakNetTime; +typedef unsigned long long RakNetTimeNS; +typedef unsigned long long RakNetTimeMS; +typedef unsigned long long RakNetTimeUS; +#endif +#else +typedef unsigned int RakNetTime; +typedef unsigned int RakNetTimeMS; +#if defined(_MSC_VER) && _MSC_VER < 1300 +typedef unsigned __int64 RakNetTimeNS; +typedef unsigned __int64 RakNetTimeUS; +#else +typedef unsigned long long RakNetTimeNS; +typedef unsigned long long RakNetTimeUS; +#endif +#endif + +#endif diff --git a/Multiplayer/raknet/RakNetTransport.h b/Multiplayer/raknet/RakNetTransport.h new file mode 100644 index 000000000..1ea3df6cf --- /dev/null +++ b/Multiplayer/raknet/RakNetTransport.h @@ -0,0 +1,130 @@ +/// \file +/// \brief Contains RakNetTransportCommandParser and RakNetTransport used to provide a secure console connection. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAKNET_TRANSPORT +#define __RAKNET_TRANSPORT + +#include "TransportInterface.h" +#include "DS_Queue.h" +#include "CommandParserInterface.h" +#include "Export.h" + +class RakPeerInterface; +class RakNetTransport; +namespace RakNet +{ + class BitStream; +} + +/// \brief RakNetTransport has its own command parser to enable remote users to change the command console's password. +class RAK_DLL_EXPORT RakNetTransportCommandParser : public CommandParserInterface +{ +public: + RakNetTransportCommandParser(); + ~RakNetTransportCommandParser(); + + /// Given \a command with parameters \a parameterList , do whatever processing you wish. + /// \param[in] command The command to process + /// \param[in] numParameters How many parameters were passed along with the command + /// \param[in] parameterList The list of parameters. parameterList[0] is the first parameter and so on. + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that sent this command. + /// \param[in] originalString The string that was actually sent over the network, in case you want to do your own parsing + bool OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, SystemAddress systemAddress, const char *originalString); + + /// You are responsible for overriding this function and returning a static string, which will identifier your parser. + /// This should return a static string + /// \return The name that you return. + const char* GetName(void) const; + + /// A callback for when you are expected to send a brief description of your parser to \a systemAddress + /// \param[in] transport The transport interface we can use to write to + /// \param[in] systemAddress The player that requested help. + void SendHelp(TransportInterface *transport, SystemAddress systemAddress); +protected: +}; + +/// \brief Use RakNetTransport if you need a secure connection between the client and the console server. +/// RakNetTransport automatically initializes security for the system. Use the project CommandConsoleClient to connect +/// To the ConsoleServer if you use RakNetTransport +class RAK_DLL_EXPORT RakNetTransport : public TransportInterface +{ +public: + RakNetTransport(); + virtual ~RakNetTransport(); + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + bool Start(unsigned short port, bool serverMode); + + /// Stop the transport provider. You can clear memory and shutdown threads here. + void Stop(void); + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + void Send( SystemAddress systemAddress, const char *data, ... ); + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + Packet* Receive( void ); + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + void DeallocatePacket( Packet *packet ); + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + void CloseConnection( SystemAddress systemAddress ); + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasNewConnection(void); + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + SystemAddress HasLostConnection(void); + + /// Sets the password which incoming connections must match. + /// While not required, it is highly recommended you set this in a real game environment or anyone can login and control your server. + /// Don't set it to a fixed value, but instead require that the server admin sets it when you start the application server + /// \param[in] password Null-terminated string to use as a password. + void SetIncomingPassword(const char *password); + + /// Returns the password set by SetIncomingPassword(). + /// \return The password set by SetIncomingPassword() + char * GetIncomingPassword(void); + + /// Returns RakNetTransportCommandParser so the console admin can change the password + CommandParserInterface* GetCommandParser(void); + +protected: + RakPeerInterface *rakPeer; + void AutoAllocate(void); + DataStructures::Queue newConnections, lostConnections; + RakNetTransportCommandParser rakNetTransportCommandParser; +}; + +#endif diff --git a/Multiplayer/raknet/RakNetTypes.h b/Multiplayer/raknet/RakNetTypes.h index 2d98a9172..b0923d099 100644 --- a/Multiplayer/raknet/RakNetTypes.h +++ b/Multiplayer/raknet/RakNetTypes.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -19,7 +19,12 @@ #define __NETWORK_TYPES_H #include "RakNetDefines.h" +#include "NativeTypes.h" +#include "RakNetTime.h" #include "Export.h" +#if !defined(_WIN32) && ((defined(__GNUC__) || defined(__GCCXML__))) +#include "stdint.h" +#endif /// Forward declaration namespace RakNet @@ -41,31 +46,34 @@ 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) -#ifdef __GET_TIME_64BIT -typedef long long RakNetTime; -typedef long long RakNetTimeNS; +typedef unsigned int BitSize_t; + +#if defined(_MSC_VER) && _MSC_VER > 0 +#define PRINTF_TIME_MODIFIER "I64" #else -typedef unsigned int RakNetTime; -typedef long long RakNetTimeNS; +#define PRINTF_TIME_MODIFIER "ll" #endif /// Describes the local socket to use for RakPeer::Startup struct RAK_DLL_EXPORT SocketDescriptor { SocketDescriptor(); - SocketDescriptor(unsigned short _port, const char *_hostAddress); + SocketDescriptor(unsigned short _port, const char *_hostAddress, bool _isPS3LobbySocket=false); /// The local port to bind to. Pass 0 to have the OS autoassign a port. unsigned short port; /// The local network card address to bind to, such as "127.0.0.1". Pass an empty string to use INADDR_ANY. char hostAddress[32]; + + /// \internal + bool isPS3LobbySocket; }; -/// \brief Unique identifier for a system. +/// \brief Network address for a system /// Corresponds to a network address +/// This is not necessarily a unique identifier. For example, if a system has both LAN and internet connections, the system may be identified by either one, depending on who is communicating +/// Use RakNetGUID for a unique per-instance of RakPeer to identify systems struct RAK_DLL_EXPORT SystemAddress { ///The peer address from inet_addr. @@ -74,8 +82,14 @@ struct RAK_DLL_EXPORT SystemAddress unsigned short port; // Return the systemAddress as a string in the format : - // Note - returns a static string. Not thread-safe or safe for multiple calls per line. - char *ToString(bool writePort=true) const; + // Returns a static string + // NOT THREADSAFE + const char *ToString(bool writePort=true) const; + + // Return the systemAddress as a string in the format : + // dest must be large enough to hold the output + // THREADSAFE + void ToString(bool writePort, char *dest) const; // Sets the binary address part from a string. Doesn't set the port void SetBinaryAddress(const char *str); @@ -93,66 +107,21 @@ struct RAK_DLL_EXPORT SystemAddress bool operator < ( const SystemAddress& 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 systemAddress portion with network sends. Takes more bandwidth, but NetworkIDs can be locally generated - // False, and only localSystemAddress is used. - static bool peerToPeerMode; - - // In peer to peer, we use both systemAddress and localSystemAddress - // In client / server, we only use localSystemAddress - SystemAddress systemAddress; - unsigned short localSystemAddress; - - 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 SystemAddress data #define SystemAddress_Size 6 -/// This represents a user message from another system. -struct Packet -{ - /// Server only - this is the index into the player array that this systemAddress maps to - SystemIndex systemIndex; - - /// The system that send this packet. - SystemAddress systemAddress; - - /// The length of the data in bytes - /// \deprecated You should use bitSize. - unsigned int length; - - /// The length of the data in bits - unsigned int bitSize; - - /// The data from the sender - unsigned char* data; - - /// @internal - /// Indicates whether to delete the data, or to simply delete the packet. - bool deleteData; -}; - class RakPeerInterface; /// All RPC functions have the same parameter list - this structure. +/// \depreciated Use the AutoRPC or RPC3 plugin instead struct RPCParameters { /// The data from the remote system unsigned char *input; /// How many bits long \a input is - unsigned int numberOfBitsOfData; + BitSize_t numberOfBitsOfData; /// Which system called this RPC SystemAddress sender; @@ -172,8 +141,36 @@ struct RPCParameters RakNet::BitStream *replyToSender; }; -/// Index of an unassigned player -const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; +/// Uniquely identifies an instance of RakPeer. Use RakPeer::GetGuidFromSystemAddress() and RakPeer::GetSystemAddressFromGuid() to go between SystemAddress and RakNetGUID +/// Use RakPeer::GetGuidFromSystemAddress(UNASSIGNED_SYSTSEM_ADDRESS) to get your own GUID +struct RAK_DLL_EXPORT RakNetGUID +{ + uint32_t g[4]; + + // Return the GUID as a string + // Returns a static string + // NOT THREADSAFE + const char *ToString(void) const; + + // Return the GUID as a string + // dest must be large enough to hold the output + // THREADSAFE + void ToString(char *dest) const; + + RakNetGUID& operator = ( const RakNetGUID& input ) + { + g[0]=input.g[0]; + g[1]=input.g[1]; + g[2]=input.g[2]; + g[3]=input.g[3]; + return *this; + } + + bool operator==( const RakNetGUID& right ) const; + bool operator!=( const RakNetGUID& right ) const; + bool operator > ( const RakNetGUID& right ) const; + bool operator < ( const RakNetGUID& right ) const; +}; /// Index of an invalid SystemAddress const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = @@ -181,12 +178,85 @@ const SystemAddress UNASSIGNED_SYSTEM_ADDRESS = 0xFFFFFFFF, 0xFFFF }; -/// Unassigned object ID -const NetworkID UNASSIGNED_NETWORK_ID = +const RakNetGUID UNASSIGNED_RAKNET_GUID = { - {0xFFFFFFFF, 0xFFFF}, 65535 + {0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF} }; +struct RAK_DLL_EXPORT NetworkID +{ + NetworkID() + { +#if defined NETWORK_ID_SUPPORTS_PEER_TO_PEER + guid = UNASSIGNED_RAKNET_GUID; + systemAddress=UNASSIGNED_SYSTEM_ADDRESS; +#endif + localSystemAddress=65535; + } + ~NetworkID() {} + + /// \depreciated. Use NETWORK_ID_SUPPORTS_PEER_TO_PEER in RakNetDefines.h + // 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 systemAddress portion with network sends. Takes more bandwidth, but NetworkIDs can be locally generated + // False, and only localSystemAddress is used. +// static bool peerToPeerMode; + +#if defined NETWORK_ID_SUPPORTS_PEER_TO_PEER + // Depreciated: Use guid instead + // In peer to peer, we use both systemAddress and localSystemAddress + // In client / server, we only use localSystemAddress + SystemAddress systemAddress; + + RakNetGUID guid; +#endif + unsigned short localSystemAddress; + + 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; +}; + +/// This represents a user message from another system. +struct Packet +{ + /// Server only - this is the index into the player array that this systemAddress maps to + SystemIndex systemIndex; + + /// The system that send this packet. + SystemAddress systemAddress; + + /// A unique identifier for the system that sent this packet, regardless of IP address (internal / external / remote system) + /// Only valid once a connection has been established (ID_CONNECTION_REQUEST_ACCEPTED, or ID_NEW_INCOMING_CONNECTION) + /// Until that time, will be UNASSIGNED_RAKNET_GUID + RakNetGUID guid; + + /// The length of the data in bytes + /// \deprecated You should use bitSize. + unsigned int length; + + /// The length of the data in bits + BitSize_t bitSize; + + /// The data from the sender + unsigned char* data; + + /// @internal + /// Indicates whether to delete the data, or to simply delete the packet. + bool deleteData; +}; + +/// Index of an unassigned player +const SystemIndex UNASSIGNED_PLAYER_INDEX = 65535; + +/// Unassigned object ID +const NetworkID UNASSIGNED_NETWORK_ID; + const int PING_TIMES_ARRAY_SIZE = 5; /// \brief RPC Function Implementation @@ -221,6 +291,7 @@ const int PING_TIMES_ARRAY_SIZE = 5; /// \def REGISTER_STATIC_RPC /// \ingroup RAKNET_RPC +/// \depreciated Use the AutoRPC plugin instead /// Register a C function as a Remote procedure. /// \param[in] networkObject Your instance of RakPeer, RakPeer, or RakPeer /// \param[in] functionName The name of the C function to call @@ -231,9 +302,11 @@ const int PING_TIMES_ARRAY_SIZE = 5; /// \def CLASS_MEMBER_ID /// \ingroup RAKNET_RPC +/// \depreciated Use the AutoRPC plugin instead /// \brief Concatenate two strings /// \def REGISTER_CLASS_MEMBER_RPC +/// \depreciated Use the AutoRPC plugin instead /// \ingroup RAKNET_RPC /// \brief Register a member function of an instantiated object as a Remote procedure call. /// RPC member Functions MUST be marked __cdecl! @@ -247,11 +320,12 @@ const int PING_TIMES_ARRAY_SIZE = 5; #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 +/// \depreciated Use the AutoRPC plugin instead /// \def UNREGISTER_STATIC_RPC /// \ingroup RAKNET_RPC +/// \depreciated Use the AutoRPC plugin instead /// Unregisters a remote procedure call /// RPC member Functions MUST be marked __cdecl! See the ObjectMemberRPC example. /// \param[in] networkObject The object that manages the function @@ -263,6 +337,7 @@ const int PING_TIMES_ARRAY_SIZE = 5; /// \def UNREGISTER_CLASS_INST_RPC /// \ingroup RAKNET_RPC +/// \depreciated Use the AutoRPC plugin instead /// \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 @@ -270,4 +345,3 @@ const int PING_TIMES_ARRAY_SIZE = 5; #define UNREGISTER_CLASS_MEMBER_RPC(networkObject, className, functionName) (networkObject)->UnregisterAsRemoteProcedureCall((#className "_" #functionName)) #endif - diff --git a/Multiplayer/raknet/RakNetVersion.h b/Multiplayer/raknet/RakNetVersion.h new file mode 100644 index 000000000..5773ec943 --- /dev/null +++ b/Multiplayer/raknet/RakNetVersion.h @@ -0,0 +1,9 @@ +#define RAKNET_VERSION "3.401" + +#define RAKNET_REVISION "$Revision$" + +#define RAKNET_DATE "2/10/2009" + +// What compatible protocol version RakNet is using. When this value changes, it indicates this version of RakNet cannot connection to an older version. +// ID_INCOMPATIBLE_PROTOCOL_VERSION will be returned on connection attempt in this case +#define RAKNET_PROTOCOL_VERSION 3 diff --git a/Multiplayer/raknet/RakNetworkFactory.h b/Multiplayer/raknet/RakNetworkFactory.h index 5858ec81f..62238ab09 100644 --- a/Multiplayer/raknet/RakNetworkFactory.h +++ b/Multiplayer/raknet/RakNetworkFactory.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free diff --git a/Multiplayer/raknet/RakPeer.h b/Multiplayer/raknet/RakPeer.h new file mode 100644 index 000000000..94752f53c --- /dev/null +++ b/Multiplayer/raknet/RakPeer.h @@ -0,0 +1,953 @@ +/// \file +/// \brief The main class used for data transmission and most of RakNet's functionality. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RAK_PEER_H +#define __RAK_PEER_H + +#include "ReliabilityLayer.h" +#include "RakPeerInterface.h" +#include "RPCNode.h" +#include "RSACrypt.h" +#include "BitStream.h" +#include "SingleProducerConsumer.h" +#include "RPCMap.h" +#include "SimpleMutex.h" +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakString.h" +#include "RakThread.h" + +class HuffmanEncodingTree; +class PluginInterface; + +// Sucks but this struct has to be outside the class. Inside and DevCPP won't let you refer to the struct as RakPeer::SystemAddressAndIndex while GCC +// forces you to do RakPeer::SystemAddressAndIndex +struct SystemAddressAndIndex{SystemAddress systemAddress;unsigned index;}; +int RAK_DLL_EXPORT SystemAddressAndIndexComp( const SystemAddress &key, const SystemAddressAndIndex &data ); // GCC requires RakPeer::SystemAddressAndIndex or it won't compile + +/// The primary interface for RakNet, RakPeer contains all major functions for the library. +/// See the individual functions for what the class can do. +/// \brief The main interface for network communications +class RAK_DLL_EXPORT RakPeer : public RakPeerInterface +{ +public: + ///Constructor + RakPeer(); + + ///Destructor + virtual ~RakPeer(); + + // --------------------------------------------------------------------------------------------Major Low Level Functions - Functions needed by most users-------------------------------------------------------------------------------------------- + /// \brief Starts the network threads, opens the listen port. + /// You must call this before calling Connect(). + /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). + /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections + /// \note Set _RAKNET_THREADSAFE in RakNetDefines.h if you want to call RakNet functions from multiple threads (not recommended, as it is much slower and RakNet is already asynchronous). + /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections + /// \param[in] localPort The port to listen for connections on. + /// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle (30 to give the game priority, 0 for regular (recommended) + /// \param[in] socketDescriptors An array of SocketDescriptor structures to force RakNet to listen on a particular IP address or port (or both). Each SocketDescriptor will represent one unique socket. Do not pass redundant structures. To listen on a specific port, you can pass SocketDescriptor(myPort,0); such as for a server. For a client, it is usually OK to just pass SocketDescriptor(); + /// \param[in] socketDescriptorCount The size of the \a socketDescriptors array. Pass 1 if you are not sure what to pass. + /// \return False on failure (can't create socket or thread), true on success. + bool Startup( unsigned short maxConnections, int _threadSleepTimer, SocketDescriptor *socketDescriptors, unsigned socketDescriptorCount ); + + /// Secures connections though a combination of SHA1, AES128, SYN Cookies, and RSA to prevent connection spoofing, replay attacks, data eavesdropping, packet tampering, and MitM attacks. + /// There is a significant amount of processing and a slight amount of bandwidth overhead for this feature. + /// If you accept connections, you must call this or else secure connections will not be enabled for incoming connections. + /// If you are connecting to another system, you can call this with values for the (e and p,q) public keys before connecting to prevent MitM + /// Define how many bits are used in RakNetDefines.h with RAKNET_RSA_FACTOR_LIMBS + /// \pre Must be called before Initialize + /// \param[in] pubKeyE A pointer to the public keys from the RSACrypt class. + /// \param[in] pubKeyN A pointer to the public keys from the RSACrypt class. + /// \param[in] privKeyP Public key generated from the RSACrypt class. + /// \param[in] privKeyQ Public key generated from the RSACrypt class. If the private keys are 0, then a new key will be generated when this function is called@see the Encryption sample + void InitializeSecurity(const char *pubKeyE, const char *pubKeyN, const char *privKeyP, const char *privKeyQ ); + + /// Disables all security. + /// \note Must be called while offline + void DisableSecurity( void ); + + /// If secure connections are on, do not use secure connections for a specific IP address. + /// This is useful if you have a fixed-address internal server behind a LAN. + /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \param[in] ip IP address to add. * wildcards are supported. + void AddToSecurityExceptionList(const char *ip); + + /// Remove a specific connection previously added via AddToSecurityExceptionList + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + void RemoveFromSecurityExceptionList(const char *ip); + + /// Checks to see if a given IP is in the security exception list + /// \param[in] IP address to check. + bool IsInSecurityExceptionList(const char *ip); + + /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, + /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, + /// it will be reduced to the maximum number of peers allowed. + /// Defaults to 0, meaning by default, nobody can connect to you + /// \param[in] numberAllowed Maximum number of incoming connections allowed. + void SetMaximumIncomingConnections( unsigned short numberAllowed ); + + /// Returns the value passed to SetMaximumIncomingConnections() + /// \return the maximum number of incoming connections, which is always <= maxConnections + unsigned short GetMaximumIncomingConnections( void ) const; + + /// Returns how many open connections there are at this time + /// \return the number of open connections + unsigned short NumberOfConnections(void) const; + + /// Sets the password incoming connections must match in the call to Connect (defaults to none). Pass 0 to passwordData to specify no password + /// This is a way to set a low level password for all incoming connections. To selectively reject connections, implement your own scheme using CloseConnection() to remove unwanted connections + /// \param[in] passwordData A data block that incoming connections must match. This can be just a password, or can be a stream of data. Specify 0 for no password data + /// \param[in] passwordDataLength The length in bytes of passwordData + void SetIncomingPassword( const char* passwordData, int passwordDataLength ); + + /// Gets the password passed to SetIncomingPassword + /// \param[out] passwordData Should point to a block large enough to hold the password data you passed to SetIncomingPassword() + /// \param[in,out] passwordDataLength Maximum size of the array passwordData. Modified to hold the number of bytes actually written + void GetIncomingPassword( char* passwordData, int *passwordDataLength ); + + /// \brief Connect to the specified host (ip or domain name) and server port. + /// Calling Connect and not calling SetMaximumIncomingConnections acts as a dedicated client. + /// Calling both acts as a true peer. This is a non-blocking connection. + /// You know the connection is successful when IsConnected() returns true or Receive() gets a message with the type identifier ID_CONNECTION_ACCEPTED. + /// If the connection is not successful, such as a rejected connection or no response then neither of these things will happen. + /// \pre Requires that you first call Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return True on successful initiation. False on incorrect parameters, internal error, or too many existing peers. Returning true does not mean you connected! + bool Connect( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, unsigned connectionSocketIndex=0 ); + + /// \brief Connect to the specified network ID (Platform specific console function) + /// Does built-in NAT traversal + /// \param[in] networkServiceId Network ID structure for the online service + /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. + /// \param[in] passwordDataLength The length in bytes of passwordData + //bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength ); + + /// \brief Stops the network threads and closes all connections. + /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. + /// \param[in] orderingChannel If blockDuration > 0, ID_DISCONNECTION_NOTIFICATION will be sent on this channel + /// If you set it to 0 then the disconnection notification won't be sent + void Shutdown( unsigned int blockDuration, unsigned char orderingChannel=0 ); + + /// Returns if the network thread is running + /// \return true if the network thread is running, false otherwise + bool IsActive( void ) const; + + /// Fills the array remoteSystems with the SystemAddress of all the systems we are connected to + /// \param[out] remoteSystems An array of SystemAddress structures to be filled with the SystemAddresss of the systems we are connected to. Pass 0 to remoteSystems to only get the number of systems we are connected to + /// \param[in, out] numberOfSystems As input, the size of remoteSystems array. As output, the number of elements put into the array + bool GetConnectionList( SystemAddress *remoteSystems, unsigned short *numberOfSystems ) const; + + /// Sends a block of data to the specified system that you are connected to. + /// This function only works while the connected + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemAddress Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \return False if we are not connected to the specified recipient. True otherwise + bool Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast ); + + /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual + /// This function works anytime + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + void SendLoopback( const char *data, const int length ); + + /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. + /// \param[in] bitStream The bitstream to send + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemAddress Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \return False if we are not connected to the specified recipient. True otherwise + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. + bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast ); + + /// Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works while the connected + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemAddress Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \return False if we are not connected to the specified recipient. True otherwise + /// \note Doesn't support the router plugin + bool SendList( char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast ); + + /// Gets a message from the incoming message queue. + /// Use DeallocatePacket() to deallocate the message after you are done with it. + /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. + /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. + /// sa RakNetTypes.h contains struct Packet + Packet* Receive( void ); + + /// Call this to deallocate a message returned by Receive() when you are done handling it. + /// \param[in] packet The message to deallocate. + void DeallocatePacket( Packet *packet ); + + /// Return the total number of connections we are allowed + unsigned short GetMaximumNumberOfPeers( void ) const; + + // --------------------------------------------------------------------------------------------Remote Procedure Call Functions - Functions to initialize and perform RPC-------------------------------------------------------------------------------------------- + /// \ingroup RAKNET_RPC + /// Register a C or static member function as available for calling as a remote procedure call + /// \param[in] uniqueID A null-terminated unique string to identify this procedure. See RegisterClassMemberRPC() for class member functions. + /// \param[in] functionPointer(...) The name of the function to be used as a function pointer. This can be called whether active or not, and registered functions stay registered unless unregistered + void RegisterAsRemoteProcedureCall( const char* uniqueID, void ( *functionPointer ) ( RPCParameters *rpcParms ) ); + + /// \ingroup RAKNET_RPC + /// Register a C++ member function as available for calling as a remote procedure call. + /// \param[in] uniqueID A null terminated string to identify this procedure. Recommended you use the macro REGISTER_CLASS_MEMBER_RPC to create the string. Use RegisterAsRemoteProcedureCall() for static functions. + /// \param[in] functionPointer The name of the function to be used as a function pointer. This can be called whether active or not, and registered functions stay registered unless unregistered with UnregisterAsRemoteProcedureCall + /// \sa The sample ObjectMemberRPC.cpp + void RegisterClassMemberRPC( const char* uniqueID, void *functionPointer ); + + /// \ingroup RAKNET_RPC + /// Unregisters a C function as available for calling as a remote procedure call that was formerly registered with RegisterAsRemoteProcedureCall. Only call offline. + /// \param[in] uniqueID A string of only letters to identify this procedure. Recommended you use the macro CLASS_MEMBER_ID for class member functions. + void UnregisterAsRemoteProcedureCall( const char* uniqueID ); + + /// \ingroup RAKNET_RPC + /// Used by Object member RPC to lookup objects given that object's ID + /// Also used by the ReplicaManager plugin + /// \param[in] An instance of NetworkIDManager to use for the loookup. + void SetNetworkIDManager( NetworkIDManager *manager ); + + /// \return Returns the value passed to SetNetworkIDManager or 0 if never called. + NetworkIDManager *GetNetworkIDManager(void) const; + + /// \ingroup RAKNET_RPC + /// Calls a C function on the remote system that was already registered using RegisterAsRemoteProcedureCall(). + /// \pre To use object member RPC (networkID!=UNASSIGNED_OBJECT_ID), The recipient must have called SetNetworkIDManager so the system can handle the object lookups + /// \param[in] uniqueID A NULL terminated string identifying the function to call. Recommended you use the macro CLASS_MEMBER_ID for class member functions. + /// \param[in] data The data to send + /// \param[in] bitLength The number of bits of \a data + /// \param[in] priority What priority level to send on. See PacketPriority.h. + /// \param[in] reliability How reliability to send this data. See PacketPriority.h. + /// \param[in] orderingChannel When using ordered or sequenced message, what channel to order these on. + /// \param[in] systemAddress Who to send this message to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] includedTimestamp Pass a timestamp if you wish, to be adjusted in the usual fashion as per ID_TIMESTAMP. Pass 0 to not include a timestamp. + /// \param[in] networkID For static functions, pass UNASSIGNED_NETWORK_ID. For member functions, you must derive from NetworkIDObject and pass the value returned by NetworkIDObject::GetNetworkID for that object. + /// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which should be remotely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed. + /// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure + bool RPC( const char* uniqueID, const char *data, BitSize_t bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RakNetTime *includedTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget ); + + /// \ingroup RAKNET_RPC + /// Calls a C function on the remote system that was already registered using RegisterAsRemoteProcedureCall. + /// If you want that function to return data you should call RPC from that system in the same wayReturns true on a successful packet + /// send (this does not indicate the recipient performed the call), false on failure + /// \pre To use object member RPC (networkID!=UNASSIGNED_OBJECT_ID), The recipient must have called SetNetworkIDManager so the system can handle the object lookups + /// \param[in] uniqueID A NULL terminated string identifying the function to call. Recommended you use the macro CLASS_MEMBER_ID for class member functions. + /// \param[in] data The data to send + /// \param[in] bitLength The number of bits of \a data + /// \param[in] priority What priority level to send on. See PacketPriority.h. + /// \param[in] reliability How reliability to send this data. See PacketPriority.h. + /// \param[in] orderingChannel When using ordered or sequenced message, what channel to order these on. + /// \param[in] systemAddress Who to send this message to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \param[in] includedTimestamp Pass a timestamp if you wish, to be adjusted in the usual fashion as per ID_TIMESTAMP. Pass 0 to not include a timestamp. + /// \param[in] networkID For static functions, pass UNASSIGNED_NETWORK_ID. For member functions, you must derive from NetworkIDObject and pass the value returned by NetworkIDObject::GetNetworkID for that object. + /// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which should be remotely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed. + /// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure + bool RPC( const char* uniqueID, const RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RakNetTime *includedTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget ); + + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- + /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). + /// \param[in] target Which system to close the connection to. + /// \param[in] sendDisconnectionNotification True to send ID_DISCONNECTION_NOTIFICATION to the recipient. False to close it silently. + /// \param[in] channel Which ordering channel to send the disconnection notification on, if any + void CloseConnection( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 ); + + /// Cancel a pending connection attempt + /// If we are already connected, the connection stays open + /// \param[in] target Which system to cancel + void CancelConnectionAttempt( const SystemAddress target ); + + /// Returns if a particular systemAddress is connected to us (this also returns true if we are in the process of connecting) + /// \param[in] systemAddress The SystemAddress we are referring to + /// \param[in] includeInProgress If true, also return true for connections that are in progress but haven't completed + /// \param[in] includeDisconnecting If true, also return true for connections that are in the process of disconnecting + /// \return True if this system is connected and active, false otherwise. + bool IsConnected(const SystemAddress systemAddress, bool includeInProgress=false, bool includeDisconnecting=false); + + /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. + /// This includes systems which were formerly connected, but are not now connected + /// \param[in] systemAddress The SystemAddress we are referring to + /// \return The index of this SystemAddress or -1 on system not found. + int GetIndexFromSystemAddress( const SystemAddress systemAddress ); + + /// This function is only useful for looping through all systems + /// Given an index, will return a SystemAddress. + /// \param[in] index Index should range between 0 and the maximum number of players allowed - 1. + /// \return The SystemAddress + SystemAddress GetSystemAddressFromIndex( int index ); + + /// Bans an IP from connecting. Banned IPs persist between connections but are not saved on shutdown nor loaded on startup. + /// param[in] IP Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will ban all IP addresses starting with 128.0.0 + /// \param[in] milliseconds how many ms for a temporary ban. Use 0 for a permanent ban + void AddToBanList( const char *IP, RakNetTime milliseconds=0 ); + + /// Allows a previously banned IP to connect. + /// param[in] Dotted IP address. Can use * as a wildcard, such as 128.0.0.* will banAll IP addresses starting with 128.0.0 + void RemoveFromBanList( const char *IP ); + + /// Allows all previously banned IPs to connect. + void ClearBanList( void ); + + /// Returns true or false indicating if a particular IP is banned. + /// \param[in] IP - Dotted IP address. + /// \return true if IP matches any IPs in the ban list, accounting for any wildcards. False otherwise. + bool IsBanned( const char *IP ); + + // --------------------------------------------------------------------------------------------Pinging Functions - Functions dealing with the automatic ping mechanism-------------------------------------------------------------------------------------------- + /// Send a ping to the specified connected system. + /// \pre The sender and recipient must already be started via a successful call to Startup() + /// \param[in] target Which system to ping + void Ping( const SystemAddress target ); + + /// Send a ping to the specified unconnected system. The remote system, if it is Initialized, will respond with ID_PONG followed by sizeof(RakNetTime) containing the system time the ping was sent.(Default is 4 bytes - See __GET_TIME_64BIT in RakNetTypes.h + /// \param[in] host Either a dotted IP address or a domain name. Can be 255.255.255.255 for LAN broadcast. + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return true on success, false on failure (unknown hostname) + bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 ); + + /// Returns the average of all ping times read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The ping time for this system, or -1 + int GetAveragePing( const SystemAddress systemAddress ); + + /// Returns the last ping time read for the specific system or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The last ping time for this system, or -1 + int GetLastPing( const SystemAddress systemAddress ) const; + + /// Returns the lowest ping time read or -1 if none read yet + /// \param[in] systemAddress Which system we are referring to + /// \return The lowest ping time for this system, or -1 + int GetLowestPing( const SystemAddress systemAddress ) const; + + /// Ping the remote systems every so often, or not. This is off by default. Can be called anytime. + /// \param[in] doPing True to start occasional pings. False to stop them. + void SetOccasionalPing( bool doPing ); + + // --------------------------------------------------------------------------------------------Static Data Functions - Functions dealing with API defined synchronized memory-------------------------------------------------------------------------------------------- + /// Sets the data to send along with a LAN server discovery or offline ping reply. + /// \a length should be under 400 bytes, as a security measure against flood attacks + /// \param[in] data a block of data to store, or 0 for none + /// \param[in] length The length of data in bytes, or 0 for none + /// \sa Ping.cpp + void SetOfflinePingResponse( const char *data, const unsigned int length ); + + /// Returns pointers to a copy of the data passed to SetOfflinePingResponse + /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + void GetOfflinePingResponse( char **data, unsigned int *length ); + + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- + /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) + /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy + SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const; + + /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port + /// (the IP / port the specified player uses to communicate with you) + /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. + SystemAddress GetExternalID( const SystemAddress target ) const; + + /// Given a connected system, give us the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// O(log2(n)) + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the system we are connected to + RakNetGUID GetGuidFromSystemAddress( const SystemAddress input ) const; + + /// Given the GUID of a connected system, give us the system address of that system. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future + /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to + SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const; + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// \param[in] timeMS Time, in MS + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. + void SetTimeoutTime( RakNetTime timeMS, const SystemAddress target ); + + /// Set the MTU per datagram. It's important to set this correctly - otherwise packets will be needlessly split, decreasing performance and throughput. + /// Maximum allowed size is MAXIMUM_MTU_SIZE. + /// Too high of a value will cause packets not to arrive at worst and be fragmented at best. + /// Too low of a value will split packets unnecessarily. + /// Recommended size is 1500 + /// sa MTUSize.h + /// \param[in] size The MTU size + /// \param[in] target Which system to set this for. UNASSIGNED_SYSTEM_ADDRESS to set the default, for new systems + /// \pre Can only be called when not connected. + /// \return false on failure (we are connected), else true + bool SetMTUSize( int size, const SystemAddress target ); + + /// Returns the current MTU size + /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default + /// \return The current MTU size + int GetMTUSize( const SystemAddress target ) const; + + /// Returns the number of IP addresses this system has internally. Get the actual addresses from GetLocalIP() + unsigned GetNumberOfAddresses( void ); + + /// Returns an IP address at index 0 to GetNumberOfAddresses-1 + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index + const char* GetLocalIP( unsigned int index ); + + /// Is this a local IP? + /// \param[in] An IP address to check, excluding the port + /// \return True if this is one of the IP addresses returned by GetLocalIP + bool IsLocalIP( const char *ip ); + + /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary + /// when connecting to servers with multiple IP addresses. + /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections + void AllowConnectionResponseIPMigration( bool allow ); + + /// Sends a one byte message ID_ADVERTISE_SYSTEM to the remote unconnected system. + /// This will tell the remote system our external IP outside the LAN along with some user data. + /// \pre The sender and recipient must already be started via a successful call to Initialize + /// \param[in] host Either a dotted IP address or a domain name + /// \param[in] remotePort Which port to connect to on the remote machine. + /// \param[in] data Optional data to append to the packet. + /// \param[in] dataLength length of data in bytes. Use 0 if no data. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \return false if IsActive()==false or the host is unresolvable. True otherwise + bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 ); + + /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. + /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived + /// As it can be slow or cumbersome to get this notification for every chunk, you can set the interval at which it is returned. + /// Defaults to 0 (never return this notification) + /// \param[in] interval How many messages to use as an interval + void SetSplitMessageProgressInterval(int interval); + + /// Returns what was passed to SetSplitMessageProgressInterval() + /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. + int GetSplitMessageProgressInterval(void) const; + + /// Set how long to wait before giving up on sending an unreliable message + /// Useful if the network is clogged up. + /// Set to 0 or less to never timeout. Defaults to 0. + /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. + void SetUnreliableTimeout(RakNetTime timeoutMS); + + /// Send a message to host, with the IP socket option TTL set to 3 + /// This message will not reach the host, but will open the router. + /// \param[in] ttl Max hops of datagram + /// Used for NAT-Punchthrough + void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 ); + + // --------------------------------------------------------------------------------------------Compression Functions - Functions related to the compression layer-------------------------------------------------------------------------------------------- + /// Enables or disables frequency table tracking. This is required to get a frequency table, which is used in GenerateCompressionLayer() + /// This value persists between connect calls and defaults to false (no frequency tracking) + /// \pre You can call this at any time - however you SHOULD only call it when disconnected. Otherwise you will only trackpart of the values sent over the network. + /// \param[in] doCompile True to enable tracking + void SetCompileFrequencyTable( bool doCompile ); + + /// Returns the frequency of outgoing bytes into output frequency table + /// The purpose is to save to file as either a master frequency table from a sample game session for passing to + /// GenerateCompressionLayer() + /// \pre You should only call this when disconnected. Requires that you first enable data frequency tracking by calling SetCompileFrequencyTable(true) + /// \param[out] outputFrequencyTable The frequency of each corresponding byte + /// \return False (failure) if connected or if frequency table tracking is not enabled. Otherwise true (success) + bool GetOutgoingFrequencyTable( unsigned int outputFrequencyTable[ 256 ] ); + + /// This is an optional function to generate the compression layer based on the input frequency table. + /// If you want to use it you should call this twice - once with inputLayer as true and once as false. + /// The frequency table passed here with inputLayer=true should match the frequency table on the recipient with inputLayer=false. + /// Likewise, the frequency table passed here with inputLayer=false should match the frequency table on the recipient with inputLayer=true. + /// Calling this function when there is an existing layer will overwrite the old layer. + /// \pre You should only call this when disconnected + /// \param[in] inputFrequencyTable A frequency table for your data + /// \param[in] inputLayer Is this the input layer? + /// \return false (failure) if connected. Otherwise true (success) + /// \sa Compression.cpp + bool GenerateCompressionLayer( unsigned int inputFrequencyTable[ 256 ], bool inputLayer ); + + /// Delete the output or input layer as specified. This is not necessary to call and is only valuable for freeing memory. + /// \pre You should only call this when disconnected + /// \param[in] inputLayer True to mean the inputLayer, false to mean the output layer + /// \return false (failure) if connected. Otherwise true (success) + bool DeleteCompressionLayer( bool inputLayer ); + + /// Returns the compression ratio. A low compression ratio is good. Compression is for outgoing data + /// \return The compression ratio + float GetCompressionRatio( void ) const; + + ///Returns the decompression ratio. A high decompression ratio is good. Decompression is for incoming data + /// \return The decompression ratio + float GetDecompressionRatio( void ) const; + + // -------------------------------------------------------------------------------------------- Plugin Functions-------------------------------------------------------------------------------------------- + /// Attatches a Plugin interface to run code automatically on message receipt in the Receive call + /// \note If plugins have dependencies on each other then the order does matter - for example the router plugin should go first because it might route messages for other plugins + /// \param[in] messageHandler Pointer to a plugin to attach + void AttachPlugin( PluginInterface *plugin ); + + /// Detaches a Plugin interface to run code automatically on message receipt + /// \param[in] messageHandler Pointer to a plugin to detach + void DetachPlugin( PluginInterface *messageHandler ); + + // --------------------------------------------------------------------------------------------Miscellaneous Functions-------------------------------------------------------------------------------------------- + /// Put a message back at the end of the receive queue in case you don't want to deal with it immediately + /// \param[in] packet The packet you want to push back. + /// \param[in] pushAtHead True to push the packet so that the next receive call returns it. False to push it at the end of the queue (obviously pushing it at the end makes the packets out of order) + void PushBackPacket( Packet *packet, bool pushAtHead ); + + /// \Internal + // \param[in] routerInterface The router to use to route messages to systems not directly connected to this system. + void SetRouterInterface( RouterInterface *routerInterface ); + + /// \Internal + // \param[in] routerInterface The router to use to route messages to systems not directly connected to this system. + void RemoveRouterInterface( RouterInterface *routerInterface ); + + /// \returns a packet for you to write to if you want to create a Packet for some reason. + /// You can add it to the receive buffer with PushBackPacket + /// \param[in] dataSize How many bytes to allocate for the buffer + /// \return A packet you can write to + Packet* AllocatePacket(unsigned dataSize); + + // --------------------------------------------------------------------------------------------Network Simulator Functions-------------------------------------------------------------------------------------------- + /// Adds simulated ping and packet loss to the outgoing data flow. + /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. + /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \depreciated Use http://www.jenkinssoftware.com/raknet/forum/index.php?topic=1671.0 instead. + /// \param[in] maxSendBPS Maximum bits per second to send. Packetloss grows linearly. 0 to disable. (CURRENTLY BROKEN - ALWAYS DISABLED) + /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. + void ApplyNetworkSimulator( double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance); + + /// Limits how much outgoing bandwidth can be sent per-connection. + /// This limit does not apply to the sum of all connections! + /// Exceeding the limit queues up outgoing traffic + /// \param[in] maxBitsPerSecond Maximum bits per second to send. Use 0 for unlimited (default). Once set, it takes effect immedately and persists until called again. + void SetPerConnectionOutgoingBandwidthLimit( unsigned maxBitsPerSecond ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + bool IsNetworkSimulatorActive( void ); + + // -------------------------------------------------------------------------------------------- Socket Functions-------------------------------------------------------------------------------------------- + /// Have RakNet use a socket you created yourself + /// The socket should not be in use - it is up to you to either shutdown or close the connections using it. Otherwise existing connections on that socket will eventually disconnect + /// This socket will be forgotten after calling Shutdown(), so rebind again if you need to. + /// \param[in] s The socket to rebind. + /// \param[in] haveRakNetCloseSocket If true, RakNet will call closeSocket on shutdown for this socket. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + void UseUserSocket( int socket, bool haveRakNetCloseSocket, unsigned connectionSocketIndex); + + /// Have RakNet recreate a socket using a different port. + /// The socket should not be in use - it is up to you to either shutdown or close the connections using it. Otherwise existing connections on that socket will eventually disconnect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \param[in] sd Address to bind on + void RebindSocketAddress(unsigned connectionSocketIndex, SocketDescriptor &sd); + + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- + + /// Returns a structure containing a large set of network statistics for the specified system. + /// You can map this data to a string using the C style StatisticsToString() function + /// \param[in] systemAddress: Which connected system to get statistics for + /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe + /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. + /// \sa RakNetStatistics.h + RakNetStatistics * const GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 ); + + // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- + /// \internal + char *GetRPCString( const char *data, const BitSize_t bitSize, const SystemAddress systemAddress); + + /// \internal + bool SendOutOfBand(const char *host, unsigned short remotePort, MessageID header, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 ); + + /// \internal + /// \brief Holds the clock differences between systems, along with the ping + struct PingAndClockDifferential + { + unsigned short pingTime; + RakNetTime clockDifferential; + }; + + /// \internal + /// \brief All the information representing a connected system system + struct RemoteSystemStruct + { + bool isActive; // Is this structure in use? + SystemAddress systemAddress; /// Their external IP on the internet + SystemAddress myExternalSystemAddress; /// Your external IP on the internet, from their perspective + SystemAddress theirInternalSystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; /// Their internal IP, behind the LAN + ReliabilityLayer reliabilityLayer; /// The reliability layer associated with this player + bool weInitiatedTheConnection; /// True if we started this connection via Connect. False if someone else connected to us. + PingAndClockDifferential pingAndClockDifferential[ PING_TIMES_ARRAY_SIZE ]; /// last x ping times and calculated clock differentials with it + int pingAndClockDifferentialWriteIndex; /// The index we are writing into the pingAndClockDifferential circular buffer + unsigned short lowestPing; ///The lowest ping value encountered + RakNetTime nextPingTime; /// When to next ping this player + RakNetTime lastReliableSend; /// When did the last reliable send occur. Reliable sends must occur at least once every timeoutTime/2 units to notice disconnects + RakNetTime connectionTime; /// connection time, if active. + unsigned char AESKey[ 16 ]; /// Security key. + bool setAESKey; /// true if security is enabled. + int connectionSocketIndex; // index into connectionSockets to send back on. + RPCMap rpcMap; /// Mapping of RPC calls to single byte integers to save transmission bandwidth. + RakNetGUID guid; + int MTUSize; +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +// void *onlineServiceId; +// unsigned int connectionId; +#endif + enum ConnectMode {NO_ACTION, DISCONNECT_ASAP, DISCONNECT_ASAP_SILENTLY, DISCONNECT_ON_NO_ACK, REQUESTED_CONNECTION, HANDLING_CONNECTION_REQUEST, UNVERIFIED_SENDER, SET_ENCRYPTION_ON_MULTIPLE_16_BYTE_PACKET, CONNECTED} connectMode; + }; + +protected: + + friend RAK_THREAD_DECLARATION(UpdateNetworkLoop); + + /* +#ifdef _WIN32 + // friend unsigned __stdcall RecvFromNetworkLoop(LPVOID arguments); + friend void __stdcall ProcessPortUnreachable( const unsigned int binaryAddress, const unsigned short port, RakPeer *rakPeer ); + friend void __stdcall ProcessNetworkPacket( const unsigned int binaryAddress, const unsigned short port, const char *data, const int length, RakPeer *rakPeer, unsigned connectionSocketIndex ); + friend unsigned __stdcall UpdateNetworkLoop( LPVOID arguments ); +#else + // friend void* RecvFromNetworkLoop( void* arguments ); + friend void ProcessPortUnreachable( const unsigned int binaryAddress, const unsigned short port, RakPeer *rakPeer ); + friend void ProcessNetworkPacket( const unsigned int binaryAddress, const unsigned short port, const char *data, const int length, RakPeer *rakPeer, unsigned connectionSocketIndex ); + friend void* UpdateNetworkLoop( void* arguments ); +#endif + */ + + friend void ProcessPortUnreachable( const unsigned int binaryAddress, const unsigned short port, RakPeer *rakPeer ); + friend void ProcessNetworkPacket( const unsigned int binaryAddress, const unsigned short port, const char *data, const int length, RakPeer *rakPeer, unsigned connectionSocketIndex ); + + // This is done to provide custom RPC handling when in a blocking RPC + Packet* ReceiveIgnoreRPC( void ); + + int GetIndexFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread ); + + //void RemoveFromRequestedConnectionsList( const SystemAddress systemAddress ); + bool SendConnectionRequest( const char* host, unsigned short remotePort, const char *passwordData, int passwordDataLength, unsigned connectionSocketIndex, unsigned int extraData ); + ///Get the reliability layer associated with a systemAddress. + /// \param[in] systemAddress The player identifier + /// \return 0 if none + RemoteSystemStruct *GetRemoteSystemFromSystemAddress( const SystemAddress systemAddress, bool calledFromNetworkThread, bool onlyActive ) const; + ///Parse out a connection request packet + void ParseConnectionRequestPacket( RakPeer::RemoteSystemStruct *remoteSystem, SystemAddress systemAddress, const char *data, int byteSize); + ///When we get a connection request from an ip / port, accept it unless full + void OnConnectionRequest( RakPeer::RemoteSystemStruct *remoteSystem, unsigned char *AESKey, bool setAESKey ); + void SendConnectionRequestAccepted(RakPeer::RemoteSystemStruct *remoteSystem); + ///Send a reliable disconnect packet to this player and disconnect them when it is delivered + void NotifyAndFlagForShutdown( const SystemAddress systemAddress, bool performImmediate, unsigned char orderingChannel ); + ///Returns how many remote systems initiated a connection to us + unsigned short GetNumberOfRemoteInitiatedConnections( void ) const; + ///Get a free remote system from the list and assign our systemAddress to it. Should only be called from the update thread - not the user thread + RemoteSystemStruct * AssignSystemAddressToRemoteSystemList( const SystemAddress systemAddress, RemoteSystemStruct::ConnectMode connectionMode, unsigned connectionSocketIndex ); + ///An incoming packet has a timestamp, so adjust it to be relative to this system + void ShiftIncomingTimestamp( unsigned char *data, SystemAddress systemAddress ) const; + ///Get the most probably accurate clock differential for a certain player + RakNetTime GetBestClockDifferential( const SystemAddress systemAddress ) const; + + //void PushPortRefused( const SystemAddress target ); + ///Handles an RPC packet. This is sending an RPC request + /// \param[in] data A packet returned from Receive with the ID ID_RPC + /// \param[in] length The size of the packet data + /// \param[in] systemAddress The sender of the packet + /// \return true on success, false on a bad packet or an unregistered function + bool HandleRPCPacket( const char *data, int length, SystemAddress systemAddress ); + + ///Handles an RPC reply packet. This is data returned from an RPC call + /// \param[in] data A packet returned from Receive with the ID ID_RPC + /// \param[in] length The size of the packet data + /// \param[in] systemAddress The sender of the packet + void HandleRPCReplyPacket( const char *data, int length, SystemAddress systemAddress ); + + ///Set this to true to terminate the Peer thread execution + volatile bool endThreads; + ///true if the peer thread is active. + volatile bool isMainLoopThreadActive; + bool occasionalPing; /// Do we occasionally ping the other systems?*/ + ///Store the maximum number of peers allowed to connect + unsigned short maximumNumberOfPeers; + //05/02/06 Just using maximumNumberOfPeers instead + ///Store the maximum number of peers able to connect, including reserved connection slots for pings, etc. + //unsigned short remoteSystemListSize; + ///Store the maximum incoming connection allowed + unsigned short maximumIncomingConnections; + RakNet::BitStream offlinePingResponse; + ///Local Player ID + SystemAddress mySystemAddress[MAXIMUM_NUMBER_OF_INTERNAL_IDS]; + char incomingPassword[256]; + unsigned char incomingPasswordLength; + + /// This is an array of pointers to RemoteSystemStruct + /// This allows us to preallocate the list when starting, so we don't have to allocate or delete at runtime. + /// Another benefit is that is lets us add and remove active players simply by setting systemAddress + /// and moving elements in the list by copying pointers variables without affecting running threads, even if they are in the reliability layer + RemoteSystemStruct* remoteSystemList; + + // remoteSystemLookup is only used in the network thread or when single threaded. Not safe to use this in the user thread + // A list of SystemAddressAndIndex sorted by systemAddress. This way, given a SystemAddress, we can quickly get the index into remoteSystemList to find this player. + // This is an optimization to avoid scanning the remoteSystemList::systemAddress member when doing targeted sends + // Improves speed of a targeted send to every player from O(n^2) to O(n * log2(n)). + // For an MMOG with 1000 players: + // The original method takes 1000^2=1,000,000 calls + // The new method takes: + // logbx = (logax) / (logab) + // log2(x) = log10(x) / log10(2) + // log2(1000) = log10(1000) / log10(2) + // log2(1000) = 9.965 + // 9.965 * 1000 = 9965 + // For 1000 players this optimization improves the speed by over 1000 times. + DataStructures::OrderedList remoteSystemLookup; + + enum + { + // Only put these mutexes in user thread functions! +#ifdef _RAKNET_THREADSAFE + transferToPacketQueue_Mutex, + packetPool_Mutex, + bufferedCommands_Mutex, + requestedConnectionList_Mutex, +#endif + offlinePingResponse_Mutex, + NUMBER_OF_RAKPEER_MUTEXES + }; + SimpleMutex rakPeerMutexes[ NUMBER_OF_RAKPEER_MUTEXES ]; + ///RunUpdateCycle is not thread safe but we don't need to mutex calls. Just skip calls if it is running already + + bool updateCycleIsRunning; + ///The list of people we have tried to connect to recently + + //DataStructures::Queue requestedConnectionsList; + ///Data that both the client and the server needs + + unsigned int bytesSentPerSecond, bytesReceivedPerSecond; + // bool isSocketLayerBlocking; + // bool continualPing,isRecvfromThreadActive,isMainLoopThreadActive, endThreads, isSocketLayerBlocking; + unsigned int validationInteger; + SimpleMutex incomingQueueMutex, banListMutex; //,synchronizedMemoryQueueMutex, automaticVariableSynchronizationMutex; + //DataStructures::Queue incomingpacketSingleProducerConsumer; //, synchronizedMemorypacketSingleProducerConsumer; + // BitStream enumerationData; + + struct BanStruct + { + char *IP; + RakNetTime timeout; // 0 for none + }; + + struct RequestedConnectionStruct + { + SystemAddress systemAddress; + RakNetTime nextRequestTime; + unsigned char requestsMade; + char *data; + unsigned short dataLength; + char outgoingPassword[256]; + unsigned char outgoingPasswordLength; + unsigned socketIndex; + unsigned int extraData; + enum {CONNECT=1, /*PING=2, PING_OPEN_CONNECTIONS=4,*/ /*ADVERTISE_SYSTEM=2*/} actionToTake; + }; + + //DataStructures::List* > automaticVariableSynchronizationList; + DataStructures::List banList; + DataStructures::List messageHandlerList; + // DataStructures::SingleProducerConsumer requestedConnectionList; + + DataStructures::Queue requestedConnectionQueue; + SimpleMutex requestedConnectionQueueMutex; + + /// Compression stuff + unsigned int frequencyTable[ 256 ]; + HuffmanEncodingTree *inputTree, *outputTree; + unsigned int rawBytesSent, rawBytesReceived, compressedBytesSent, compressedBytesReceived; + // void DecompressInput(RakNet::BitStream *bitStream); + // void UpdateOutgoingFrequencyTable(RakNet::BitStream * bitStream); + void GenerateSYNCookieRandomNumber( void ); + void SecuredConnectionResponse( const SystemAddress systemAddress ); + void SecuredConnectionConfirmation( RakPeer::RemoteSystemStruct * remoteSystem, char* data ); + bool RunUpdateCycle( void ); + // void RunMutexedUpdateCycle(void); + + struct BufferedCommandStruct + { + BitSize_t numberOfBitsToSend; + PacketPriority priority; + PacketReliability reliability; + char orderingChannel; + SystemAddress systemAddress; + bool broadcast; + RemoteSystemStruct::ConnectMode connectionMode; + NetworkID networkID; + bool blockingCommand; // Only used for RPC + char *data; + bool haveRakNetCloseSocket; + unsigned connectionSocketIndex; + bool isPS3LobbySocket; + SOCKET socket; + unsigned short port; + enum {BCS_SEND, BCS_CLOSE_CONNECTION, BCS_USE_USER_SOCKET, BCS_REBIND_SOCKET_ADDRESS, /*BCS_RPC, BCS_RPC_SHIFT,*/ BCS_DO_NOTHING} command; + }; + + // Single producer single consumer queue using a linked list + //BufferedCommandStruct* bufferedCommandReadIndex, bufferedCommandWriteIndex; + DataStructures::SingleProducerConsumer bufferedCommands; + + bool AllowIncomingConnections(void) const; + + void PingInternal( const SystemAddress target, bool performImmediate, PacketReliability reliability ); + bool ValidSendTarget(SystemAddress systemAddress, bool broadcast); + // This stores the user send calls to be handled by the update thread. This way we don't have thread contention over systemAddresss + void CloseConnectionInternal( const SystemAddress target, bool sendDisconnectionNotification, bool performImmediate, unsigned char orderingChannel ); + void SendBuffered( const char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode ); + void SendBufferedList( char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RemoteSystemStruct::ConnectMode connectionMode ); + bool SendImmediate( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, bool useCallerDataAllocation, RakNetTimeNS currentTime ); + //bool HandleBufferedRPC(BufferedCommandStruct *bcs, RakNetTime time); + void ClearBufferedCommands(void); + void ClearRequestedConnectionList(void); + void AddPacketToProducer(Packet *p); + unsigned int GenerateSeedFromGuid(void); + SimpleMutex securityExceptionMutex; + + //DataStructures::AVLBalancedBinarySearchTree rpcTree; + RPCMap rpcMap; // Can't use StrPtrHash because runtime insertions will screw up the indices + int defaultMTUSize; + bool trackFrequencyTable; + int threadSleepTimer; + + struct RakNetSocket + { + SOCKET s; + bool isPS3LobbySocket; + bool haveRakNetCloseSocket; + } *connectionSockets; + +// SOCKET *connectionSockets; +// bool *haveRakNetCloseSocket; // array of bools, to dealloc connectionSockets into the same index + unsigned connectionSocketsLength; + +#if defined (_WIN32) && defined(USE_WAIT_FOR_MULTIPLE_EVENTS) + WSAEVENT recvEvent; +#endif + + // Used for RPC replies + RakNet::BitStream *replyFromTargetBS; + SystemAddress replyFromTargetPlayer; + bool replyFromTargetBroadcast; + + RakNetTime defaultTimeoutTime; + + // Problem: + // Waiting in function A: + // Wait function gets RPC B: + // + bool blockOnRPCReply; + + // For redirecting sends through the router plugin. Unfortunate I have to use this architecture. + RouterInterface *router; + + // Generate and store a unique GUID + void GenerateGUID(void); + RakNetGUID guid; + + unsigned maxOutgoingBPS; + + // Nobody would use the internet simulator in a final build. +#ifndef _RELEASE + double _maxSendBPS; + unsigned short _minExtraPing, _extraPingVariance; +#endif + +#if !defined(_XBOX) && !defined(_WIN32_WCE) && !defined(X360) + /// Encryption and security + RSACrypt rsacrypt; + uint32_t publicKeyE; + uint32_t publicKeyN[RAKNET_RSA_FACTOR_LIMBS]; + bool keysLocallyGenerated, usingSecurity; + RakNetTime randomNumberExpirationTime; + unsigned char newRandomNumber[ 20 ], oldRandomNumber[ 20 ]; + + + /* + big::RSACrypt rsacrypt; + big::u32 publicKeyE; + RSA_BIT_SIZE publicKeyN; + bool keysLocallyGenerated, usingSecurity; + RakNetTime randomNumberExpirationTime; + unsigned char newRandomNumber[ 20 ], oldRandomNumber[ 20 ]; + */ +#endif + + ///How long it has been since things were updated by a call to receiveUpdate thread uses this to determine how long to sleep for + //unsigned int lastUserUpdateCycle; + /// True to allow connection accepted packets from anyone. False to only allow these packets from servers we requested a connection to. + bool allowConnectionResponseIPMigration; + + SystemAddress firstExternalID; + int splitMessageProgressInterval; + RakNetTime unreliableTimeout; +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +// unsigned int console2ContextId; +#endif + + // The packetSingleProducerConsumer transfers the packets from the network thread to the user thread. The pushedBackPacket holds packets that couldn't be processed + // immediately while waiting on blocked RPCs + DataStructures::SingleProducerConsumer packetSingleProducerConsumer; + //DataStructures::Queue pushedBackPacket, outOfOrderDeallocatedPacket; + // A free-list of packets, to reduce memory fragmentation + DataStructures::Queue packetPool; + // Used for object lookup for RPC (actually depreciated, since RPC is depreciated) + NetworkIDManager *networkIDManager; + // Systems in this list will not go through the secure connection process, even when secure connections are turned on. Wildcards are accepted. + DataStructures::List securityExceptionList; + +}; + +#endif diff --git a/Multiplayer/raknet/RakPeerInterface.h b/Multiplayer/raknet/RakPeerInterface.h index 523dcc4f0..4cae5e299 100644 --- a/Multiplayer/raknet/RakPeerInterface.h +++ b/Multiplayer/raknet/RakPeerInterface.h @@ -8,7 +8,7 @@ /// 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 +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html /// Custom license users are subject to the terms therein. /// GPL license users are subject to the GNU General Public /// License as published by the Free @@ -20,6 +20,7 @@ #include "PacketPriority.h" #include "RakNetTypes.h" +#include "RakMemoryOverride.h" #include "Export.h" // Forward declarations @@ -47,6 +48,7 @@ public: /// You must call this before calling Connect(). /// Multiple calls while already active are ignored. To call this function again with different settings, you must first call Shutdown(). /// \note Call SetMaximumIncomingConnections if you want to accept incoming connections + /// \note Set _RAKNET_THREADSAFE in RakNetDefines.h if you want to call RakNet functions from multiple threads (not recommended, as it is much slower and RakNet is already asynchronous). /// \param[in] maxConnections The maximum number of connections between this instance of RakPeer and another instance of RakPeer. Required so the network can preallocate and for thread safety. A pure client would set this to 1. A pure server would set it to the number of allowed clients.- A hybrid would set it to the sum of both types of connections /// \param[in] localPort The port to listen for connections on. /// \param[in] _threadSleepTimer How many ms to Sleep each internal update cycle (30 to give the game priority, 0 for regular (recommended) @@ -70,9 +72,24 @@ public: /// \note Must be called while offline virtual void DisableSecurity( void )=0; + /// If secure connections are on, do not use secure connections for a specific IP address. + /// This is useful if you have a fixed-address internal server behind a LAN. + /// \note Secure connections are determined by the recipient of an incoming connection. This has no effect if called on the system attempting to connect. + /// \param[in] ip IP address to add. * wildcards are supported. + virtual void AddToSecurityExceptionList(const char *ip)=0; + + /// Remove a specific connection previously added via AddToSecurityExceptionList + /// \param[in] ip IP address to remove. Pass 0 to remove all IP addresses. * wildcards are supported. + virtual void RemoveFromSecurityExceptionList(const char *ip)=0; + + /// Checks to see if a given IP is in the security exception list + /// \param[in] IP address to check. + virtual bool IsInSecurityExceptionList(const char *ip)=0; + /// Sets how many incoming connections are allowed. If this is less than the number of players currently connected, /// no more players will be allowed to connect. If this is greater than the maximum number of peers allowed, - /// it will be reduced to the maximum number of peers allowed. Defaults to 0. + /// it will be reduced to the maximum number of peers allowed. + /// Defaults to 0, meaning by default, nobody can connect to you /// \param[in] numberAllowed Maximum number of incoming connections allowed. virtual void SetMaximumIncomingConnections( unsigned short numberAllowed )=0; @@ -113,7 +130,7 @@ public: /// Does built-in NAt traversal /// \param[in] passwordData A data block that must match the data block on the server passed to SetIncomingPassword. This can be a string or can be a stream of data. Use 0 for no password. /// \param[in] passwordDataLength The length in bytes of passwordData - virtual bool Console2Connect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; + //virtual bool Console2LobbyConnect( void *networkServiceId, const char *passwordData, int passwordDataLength )=0; /// \brief Stops the network threads and closes all connections. /// \param[in] blockDuration How long, in milliseconds, you should wait for all remaining messages to go out, including ID_DISCONNECTION_NOTIFICATION. If 0, it doesn't wait at all. @@ -132,6 +149,7 @@ public: /// Sends a block of data to the specified system that you are connected to. /// This function only works while the connected + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM /// \param[in] data The block of data to send /// \param[in] length The size in bytes of the data to send /// \param[in] priority What priority level to send on. See PacketPriority.h @@ -142,6 +160,13 @@ public: /// \return False if we are not connected to the specified recipient. True otherwise virtual bool Send( const char *data, const int length, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast )=0; + /// "Send" to yourself rather than a remote system. The message will be processed through the plugins and returned to the game as usual + /// This function works anytime + /// The first byte should be a message identifier starting at ID_USER_PACKET_ENUM + /// \param[in] data The block of data to send + /// \param[in] length The size in bytes of the data to send + virtual void SendLoopback( const char *data, const int length )=0; + /// Sends a block of data to the specified system that you are connected to. Same as the above version, but takes a BitStream as input. /// \param[in] bitStream The bitstream to send /// \param[in] priority What priority level to send on. See PacketPriority.h @@ -150,12 +175,35 @@ public: /// \param[in] systemAddress Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. /// \return False if we are not connected to the specified recipient. True otherwise + /// \note COMMON MISTAKE: When writing the first byte, bitStream->Write((unsigned char) ID_MY_TYPE) be sure it is casted to a byte, and you are not writing a 4 byte enumeration. virtual bool Send( const RakNet::BitStream * bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast )=0; + /// Sends multiple blocks of data, concatenating them automatically. + /// + /// This is equivalent to: + /// RakNet::BitStream bs; + /// bs.WriteAlignedBytes(block1, blockLength1); + /// bs.WriteAlignedBytes(block2, blockLength2); + /// bs.WriteAlignedBytes(block3, blockLength3); + /// Send(&bs, ...) + /// + /// This function only works while the connected + /// \param[in] data An array of pointers to blocks of data + /// \param[in] lengths An array of integers indicating the length of each block of data + /// \param[in] numParameters Length of the arrays data and lengths + /// \param[in] priority What priority level to send on. See PacketPriority.h + /// \param[in] reliability How reliability to send this data. See PacketPriority.h + /// \param[in] orderingChannel When using ordered or sequenced messages, what channel to order these on. Messages are only ordered relative to other messages on the same stream + /// \param[in] systemAddress Who to send this packet to, or in the case of broadcasting who not to send it to. Use UNASSIGNED_SYSTEM_ADDRESS to specify none + /// \param[in] broadcast True to send this packet to all connected systems. If true, then systemAddress specifies who not to send the packet to. + /// \return False if we are not connected to the specified recipient. True otherwise + virtual bool SendList( char **data, const int *lengths, const int numParameters, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast )=0; + /// Gets a message from the incoming message queue. /// Use DeallocatePacket() to deallocate the message after you are done with it. /// User-thread functions, such as RPC calls and the plugin function PluginInterface::Update occur here. /// \return 0 if no packets are waiting to be handled, otherwise a pointer to a packet. + /// \note COMMON MISTAKE: Be sure to call this in a loop, once per game tick, until it returns 0. If you only process one packet per game tick they will buffer up. /// sa RakNetTypes.h contains struct Packet virtual Packet* Receive( void )=0; @@ -169,12 +217,14 @@ public: // --------------------------------------------------------------------------------------------Remote Procedure Call Functions - Functions to initialize and perform RPC-------------------------------------------------------------------------------------------- /// \ingroup RAKNET_RPC + /// \depreciated Use the AutoRPC plugin instead /// Register a C or static member function as available for calling as a remote procedure call /// \param[in] uniqueID A null-terminated unique string to identify this procedure. See RegisterClassMemberRPC() for class member functions. /// \param[in] functionPointer(...) The name of the function to be used as a function pointer. This can be called whether active or not, and registered functions stay registered unless unregistered virtual void RegisterAsRemoteProcedureCall( const char* uniqueID, void ( *functionPointer ) ( RPCParameters *rpcParms ) )=0; /// \ingroup RAKNET_RPC + /// \depreciated Use the AutoRPC plugin instead /// Register a C++ member function as available for calling as a remote procedure call. /// \param[in] uniqueID A null terminated string to identify this procedure. Recommended you use the macro REGISTER_CLASS_MEMBER_RPC to create the string. Use RegisterAsRemoteProcedureCall() for static functions. /// \param[in] functionPointer The name of the function to be used as a function pointer. This can be called whether active or not, and registered functions stay registered unless unregistered with UnregisterAsRemoteProcedureCall @@ -182,6 +232,7 @@ public: virtual void RegisterClassMemberRPC( const char* uniqueID, void *functionPointer )=0; /// \ingroup RAKNET_RPC + /// \depreciated Use the AutoRPC plugin instead /// Unregisters a C function as available for calling as a remote procedure call that was formerly registered with RegisterAsRemoteProcedureCall. Only call offline. /// \param[in] uniqueID A string of only letters to identify this procedure. Recommended you use the macro CLASS_MEMBER_ID for class member functions. virtual void UnregisterAsRemoteProcedureCall( const char* uniqueID )=0; @@ -209,8 +260,8 @@ public: /// \param[in] networkID For static functions, pass UNASSIGNED_NETWORK_ID. For member functions, you must derive from NetworkIDObject and pass the value returned by NetworkIDObject::GetNetworkID for that object. /// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which should be remotely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed. /// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure - virtual bool RPC( const char* uniqueID, const char *data, unsigned int bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RakNetTime *includedTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget )=0; - + virtual bool RPC( const char* uniqueID, const char *data, BitSize_t bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RakNetTime *includedTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget )=0; + /// \ingroup RAKNET_RPC /// Calls a C function on the remote system that was already registered using RegisterAsRemoteProcedureCall. /// If you want that function to return data you should call RPC from that system in the same wayReturns true on a successful packet @@ -228,7 +279,7 @@ public: /// \param[in] replyFromTarget If 0, this function is non-blocking. Otherwise it will block while waiting for a reply from the target procedure, which should be remotely written to RPCParameters::replyToSender and copied to replyFromTarget. The block will return early on disconnect or if the sent packet is unreliable and more than 3X the ping has elapsed. /// \return True on a successful packet send (this does not indicate the recipient performed the call), false on failure virtual bool RPC( const char* uniqueID, const RakNet::BitStream *bitStream, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress, bool broadcast, RakNetTime *includedTimestamp, NetworkID networkID, RakNet::BitStream *replyFromTarget )=0; - + // -------------------------------------------------------------------------------------------- Connection Management Functions-------------------------------------------------------------------------------------------- /// Close the connection to another host (if we initiated the connection it will disconnect, if they did it will kick them out). /// \param[in] target Which system to close the connection to. @@ -236,10 +287,17 @@ public: /// \param[in] channel Which ordering channel to send the disconnection notification on, if any virtual void CloseConnection( const SystemAddress target, bool sendDisconnectionNotification, unsigned char orderingChannel=0 )=0; - /// Returns if a particular systemAddress is connected to us + /// Cancel a pending connection attempt + /// If we are already connected, the connection stays open + /// \param[in] target Which system to cancel + virtual void CancelConnectionAttempt( const SystemAddress target )=0; + + /// Returns if a particular systemAddress is connected to us (this also returns true if we are in the process of connecting) /// \param[in] systemAddress The SystemAddress we are referring to + /// \param[in] includeInProgress If true, also return true for connections that are in progress but haven't completed + /// \param[in] includeDisconnecting If true, also return true for connections that are in the process of disconnecting /// \return True if this system is connected and active, false otherwise. - virtual bool IsConnected(const SystemAddress systemAddress)=0; + virtual bool IsConnected(const SystemAddress systemAddress, bool includeInProgress=false, bool includeDisconnecting=false)=0; /// Given a systemAddress, returns an index from 0 to the maximum number of players allowed - 1. /// \param[in] systemAddress The SystemAddress we are referring to @@ -280,7 +338,8 @@ public: /// \param[in] remotePort Which port to connect to on the remote machine. /// \param[in] onlyReplyOnAcceptingConnections Only request a reply if the remote system is accepting connections /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - virtual void Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; + /// \return true on success, false on failure (unknown hostname) + virtual bool Ping( const char* host, unsigned short remotePort, bool onlyReplyOnAcceptingConnections, unsigned connectionSocketIndex=0 )=0; /// Returns the average of all ping times read for the specific system or -1 if none read yet /// \param[in] systemAddress Which system we are referring to @@ -309,20 +368,43 @@ public: /// \sa Ping.cpp virtual void SetOfflinePingResponse( const char *data, const unsigned int length )=0; + /// Returns pointers to a copy of the data passed to SetOfflinePingResponse + /// \param[out] data A pointer to a copy of the data passed to \a SetOfflinePingResponse() + /// \param[out] length A pointer filled in with the length parameter passed to SetOfflinePingResponse() + /// \sa SetOfflinePingResponse + virtual void GetOfflinePingResponse( char **data, unsigned int *length )=0; + //--------------------------------------------------------------------------------------------Network Functions - Functions dealing with the network in general-------------------------------------------------------------------------------------------- - /// Return the unique address identifier that represents you on the the network and is based on your local IP / port. + /// Return the unique address identifier that represents you or another system on the the network and is based on your local IP / port. + /// \param[in] systemAddress Use UNASSIGNED_SYSTEM_ADDRESS to get your behind-LAN address. Use a connected system to get their behind-LAN address + /// \param[in] index When you have multiple internal IDs, which index to return? Currently limited to MAXIMUM_NUMBER_OF_INTERNAL_IDS (so the maximum value of this variable is MAXIMUM_NUMBER_OF_INTERNAL_IDS-1) /// \return the identifier of your system internally, which may not be how other systems see if you if you are behind a NAT or proxy - virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS ) const=0; + virtual SystemAddress GetInternalID( const SystemAddress systemAddress=UNASSIGNED_SYSTEM_ADDRESS, const int index=0 ) const=0; /// Return the unique address identifier that represents you on the the network and is based on your externalIP / port /// (the IP / port the specified player uses to communicate with you) /// \param[in] target Which remote system you are referring to for your external ID. Usually the same for all systems, unless you have two or more network cards. virtual SystemAddress GetExternalID( const SystemAddress target ) const=0; + /// Given a connected system, give us the unique GUID representing that instance of RakPeer. + /// This will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// If \a input is UNASSIGNED_SYSTEM_ADDRESS, will return your own GUID + /// \pre Call Startup() first, or the function will return UNASSIGNED_RAKNET_GUID + /// \param[in] input The system address of the system we are connected to + virtual RakNetGUID GetGuidFromSystemAddress( const SystemAddress input ) const=0; + + /// Given the GUID of a connected system, give us the system address of that system. + /// The GUID will be the same on all systems connected to that instance of RakPeer, even if the external system addresses are different + /// Currently O(log(n)), but this may be improved in the future. If you use this frequently, you may want to cache the value as it won't change. + /// If \a input is UNASSIGNED_RAKNET_GUID, will return UNASSIGNED_SYSTEM_ADDRESS + /// \param[in] input The RakNetGUID of the system we are checking to see if we are connected to + virtual SystemAddress GetSystemAddressFromGuid( const RakNetGUID input ) const=0; + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable message. /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. /// \param[in] timeMS Time, in MS - /// \param[in] target Which system to do this for + /// \param[in] target Which system to do this for. Pass UNASSIGNED_SYSTEM_ADDRESS for all systems. virtual void SetTimeoutTime( RakNetTime timeMS, const SystemAddress target )=0; /// Set the MTU per datagram. It's important to set this correctly - otherwise packets will be needlessly split, decreasing performance and throughput. @@ -332,9 +414,10 @@ public: /// Recommended size is 1500 /// sa MTUSize.h /// \param[in] size The MTU size + /// \param[in] target Which system to set this for. UNASSIGNED_SYSTEM_ADDRESS to set the default, for new systems /// \pre Can only be called when not connected. /// \return false on failure (we are connected), else true - virtual bool SetMTUSize( int size )=0; + virtual bool SetMTUSize( int size, const SystemAddress target=UNASSIGNED_SYSTEM_ADDRESS )=0; /// Returns the current MTU size /// \param[in] target Which system to get this for. UNASSIGNED_SYSTEM_ADDRESS to get the default @@ -345,8 +428,15 @@ public: virtual unsigned GetNumberOfAddresses( void )=0; /// Returns an IP address at index 0 to GetNumberOfAddresses-1 + /// \param[in] index index into the list of IP addresses + /// \return The local IP address at this index virtual const char* GetLocalIP( unsigned int index )=0; + /// Is this a local IP? + /// \param[in] An IP address to check, excluding the port + /// \return True if this is one of the IP addresses returned by GetLocalIP + virtual bool IsLocalIP( const char *ip )=0; + /// Allow or disallow connection responses from any IP. Normally this should be false, but may be necessary /// when connecting to servers with multiple IP addresses. /// \param[in] allow - True to allow this behavior, false to not allow. Defaults to false. Value persists between connections @@ -360,7 +450,8 @@ public: /// \param[in] data Optional data to append to the packet. /// \param[in] dataLength length of data in bytes. Use 0 if no data. /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. - virtual void AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; + /// \return false if IsActive()==false or the host is unresolvable. True otherwise + virtual bool AdvertiseSystem( const char *host, unsigned short remotePort, const char *data, int dataLength, unsigned connectionSocketIndex=0 )=0; /// Controls how often to return ID_DOWNLOAD_PROGRESS for large message downloads. /// ID_DOWNLOAD_PROGRESS is returned to indicate a new partial message chunk, roughly the MTU size, has arrived @@ -369,16 +460,20 @@ public: /// \param[in] interval How many messages to use as an interval virtual void SetSplitMessageProgressInterval(int interval)=0; + /// Returns what was passed to SetSplitMessageProgressInterval() + /// \return What was passed to SetSplitMessageProgressInterval(). Default to 0. + virtual int GetSplitMessageProgressInterval(void) const=0; + /// Set how long to wait before giving up on sending an unreliable message /// Useful if the network is clogged up. /// Set to 0 or less to never timeout. Defaults to 0. /// \param[in] timeoutMS How many ms to wait before simply not sending an unreliable message. virtual void SetUnreliableTimeout(RakNetTime timeoutMS)=0; - /// Send a message to host, with the IP socket option TTL set to 1 + /// Send a message to host, with the IP socket option TTL set to 3 /// This message will not reach the host, but will open the router. /// Used for NAT-Punchthrough - virtual void SendTTL1( const char* host, unsigned short remotePort, unsigned connectionSocketIndex=0 )=0; + virtual void SendTTL( const char* host, unsigned short remotePort, int ttl, unsigned connectionSocketIndex=0 )=0; // --------------------------------------------------------------------------------------------Compression Functions - Functions related to the compression layer-------------------------------------------------------------------------------------------- /// Enables or disables frequency table tracking. This is required to get a frequency table, which is used in GenerateCompressionLayer() @@ -445,7 +540,7 @@ public: // \param[in] routerInterface The router to use to route messages to systems not directly connected to this system. virtual void RemoveRouterInterface( RouterInterface *routerInterface )=0; - /// \Returns a packet for you to write to if you want to create a Packet for some reason. + /// \returns a packet for you to write to if you want to create a Packet for some reason. /// You can add it to the receive buffer with PushBackPacket /// \param[in] dataSize How many bytes to allocate for the buffer /// \return A packet you can write to @@ -455,8 +550,10 @@ public: /// Adds simulated ping and packet loss to the outgoing data flow. /// To simulate bi-directional ping and packet loss, you should call this on both the sender and the recipient, with half the total ping and maxSendBPS value on each. /// You can exclude network simulator code with the _RELEASE #define to decrease code size + /// \depreciated Use http://www.jenkinssoftware.com/raknet/forum/index.php?topic=1671.0 instead. /// \param[in] maxSendBPS Maximum bits per second to send. Packetloss grows linearly. 0 to disable. (CURRENTLY BROKEN - ALWAYS DISABLED) /// \param[in] minExtraPing The minimum time to delay sends. + /// \param[in] extraPingVariance The additional random time to delay sends. virtual void ApplyNetworkSimulator( double maxSendBPS, unsigned short minExtraPing, unsigned short extraPingVariance)=0; /// Limits how much outgoing bandwidth can be sent per-connection. @@ -469,19 +566,37 @@ public: /// \return If you previously called ApplyNetworkSimulator virtual bool IsNetworkSimulatorActive( void )=0; + // -------------------------------------------------------------------------------------------- Socket Functions-------------------------------------------------------------------------------------------- + /// Have RakNet use a socket you created yourself + /// The socket should not be in use - it is up to you to either shutdown or close the connections using it. Otherwise existing connections on that socket will eventually disconnect + /// This socket will be forgotten after calling Shutdown(), so rebind again if you need to. + /// \param[in] s The socket to rebind. + /// \param[in] haveRakNetCloseSocket If true, RakNet will call closeSocket on shutdown for this socket. + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + virtual void UseUserSocket( int socket, bool haveRakNetCloseSocket, unsigned connectionSocketIndex)=0; + + /// Have RakNet recreate a socket using a different port. + /// The socket should not be in use - it is up to you to either shutdown or close the connections using it. Otherwise existing connections on that socket will eventually disconnect + /// \param[in] connectionSocketIndex Index into the array of socket descriptors passed to socketDescriptors in RakPeer::Startup() to send on. + /// \param[in] sd Address to bind on + virtual void RebindSocketAddress(unsigned connectionSocketIndex, SocketDescriptor &sd)=0; + // --------------------------------------------------------------------------------------------Statistical Functions - Functions dealing with API performance-------------------------------------------------------------------------------------------- /// Returns a structure containing a large set of network statistics for the specified system. /// You can map this data to a string using the C style StatisticsToString() function /// \param[in] systemAddress: Which connected system to get statistics for + /// \param[in] rns If you supply this structure, it will be written to it. Otherwise it will use a static struct, which is not threadsafe /// \return 0 on can't find the specified system. A pointer to a set of data otherwise. /// \sa RakNetStatistics.h - virtual RakNetStatistics * const GetStatistics( const SystemAddress systemAddress )=0; + virtual RakNetStatistics * const GetStatistics( const SystemAddress systemAddress, RakNetStatistics *rns=0 )=0; // --------------------------------------------------------------------------------------------EVERYTHING AFTER THIS COMMENT IS FOR INTERNAL USE ONLY-------------------------------------------------------------------------------------------- /// \internal - virtual char *GetRPCString( const char *data, const unsigned int bitSize, const SystemAddress systemAddress)=0; + virtual char *GetRPCString( const char *data, const BitSize_t bitSize, const SystemAddress systemAddress)=0; + /// \internal + virtual bool SendOutOfBand(const char *host, unsigned short remotePort, MessageID header, const char *data, BitSize_t dataLength, unsigned connectionSocketIndex=0 )=0; }; diff --git a/Multiplayer/raknet/RakSleep.h b/Multiplayer/raknet/RakSleep.h index 81565c23d..f1a41480d 100644 --- a/Multiplayer/raknet/RakSleep.h +++ b/Multiplayer/raknet/RakSleep.h @@ -1,6 +1,8 @@ #ifndef __RAK_SLEEP_H #define __RAK_SLEEP_H -void RakSleep(unsigned int ms); +#include "Export.h" + +void RAK_DLL_EXPORT RakSleep(unsigned int ms); #endif diff --git a/Multiplayer/raknet/RakString.h b/Multiplayer/raknet/RakString.h new file mode 100644 index 000000000..c4613333a --- /dev/null +++ b/Multiplayer/raknet/RakString.h @@ -0,0 +1,231 @@ +#ifndef __RAK_STRING_H +#define __RAK_STRING_H + +#include "Export.h" +#include "DS_List.h" +#include "RakNetTypes.h" // int64_t +#include + +namespace RakNet +{ + +class BitStream; + +/// \brief String class +/// Has the following improvements over std::string +/// Reference counting: Suitable to store in lists +/// Varidic assignment operator +/// Doesn't cause linker errors +class RAK_DLL_EXPORT RakString +{ +public: + /// Constructors + RakString(); + RakString(char input); + RakString(unsigned char input); + RakString(const unsigned char *format, ...); + RakString(const char *format, ...); + ~RakString(); + RakString( const RakString & rhs); + + /// Implicit return of const char* + operator const char* () const {return sharedString->c_str;} + + /// Same as std::string::c_str + const char *C_String(void) const {return sharedString->c_str;} + + // Lets you modify the string. Do not make the string longer - however, you can make it shorter, or change the contents. + char *C_StringUnsafe(void) {Clone(); return sharedString->c_str;} + + /// Assigment operators + RakString& operator = ( const RakString& rhs ); + RakString& operator = ( const char *str ); + RakString& operator = ( char *str ); + RakString& operator = ( const char c ); + + /// Concatenation + RakString& operator +=( const RakString& rhs); + RakString& operator += ( const char *str ); + RakString& operator += ( char *str ); + RakString& operator += ( const char c ); + + /// Character index. Do not use to change the string however. + unsigned char operator[] ( const unsigned int position ) const; + + /// Equality + bool operator==(const RakString &rhs) const; + bool operator==(const char *str) const; + bool operator==(char *str) const; + + // Comparison + bool operator < ( const RakString& right ) const; + bool operator <= ( const RakString& right ) const; + bool operator > ( const RakString& right ) const; + bool operator >= ( const RakString& right ) const; + + /// Inequality + bool operator!=(const RakString &rhs) const; + + /// Change all characters to lowercase + const char * ToLower(void); + + /// Change all characters to uppercase + const char * ToUpper(void); + + /// Set the value of the string + void Set(const char *format, ...); + + /// Returns if the string is empty. Also, C_String() would return "" + bool IsEmpty(void) const; + + /// Returns the length of the string + size_t GetLength(void) const; + + /// Replace character(s) in starting at index, for count, with c + void Replace(unsigned index, unsigned count, unsigned char c); + + /// Replace character at index with c + void SetChar( unsigned index, unsigned char c ); + + /// Replace character at index with string s + void SetChar( unsigned index, RakNet::RakString s ); + + // Gets the substring starting at index for count characters + RakString SubStr(unsigned int index, unsigned int count) const; + + /// Erase characters out of the string at index for count + void Erase(unsigned int index, unsigned int count); + + /// Compare strings (case sensitive) + int StrCmp(const RakString &rhs) const; + + /// Compare strings (not case sensitive) + int StrICmp(const RakString &rhs) const; + + /// Clear the string + void Clear(void); + + /// Print the string to the screen + void Printf(void); + + /// Print the string to a file + void FPrintf(FILE *fp); + + /// Does the given IP address match the IP address encoded into this string, accounting for wildcards? + bool IPAddressMatch(const char *IP); + + /// Does the string contain non-printable characters other than spaces? + bool ContainsNonprintableExceptSpaces(void) const; + + /// Is this a valid email address? + bool IsEmailAddress(void) const; + + /// URL Encode the string. See http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/ + void URLEncode(void); + + /// RakString uses a freeList of old no-longer used strings + /// Call this function to clear this memory on shutdown + static void FreeMemory(void); + /// \internal + static void FreeMemoryNoMutex(void); + + /// Serialize to a bitstream, uncompressed (slightly faster) + /// \param[out] bs Bitstream to serialize to + void Serialize(BitStream *bs); + + /// Static version of the Serialize function + static void Serialize(const char *str, BitStream *bs); + + /// Serialize to a bitstream, compressed (better bandwidth usage) + /// \param[out] bs Bitstream to serialize to + /// \param[in] languageId languageId to pass to the StringCompressor class + /// \param[in] writeLanguageId encode the languageId variable in the stream. If false, 0 is assumed, and DeserializeCompressed will not look for this variable in the stream (saves bandwidth) + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + void SerializeCompressed(BitStream *bs, int languageId=0, bool writeLanguageId=false); + + /// Static version of the SerializeCompressed function + static void SerializeCompressed(const char *str, BitStream *bs, int languageId=0, bool writeLanguageId=false); + + /// Deserialize what was written by Serialize + /// \param[in] bs Bitstream to serialize from + /// \return true if the deserialization was successful + bool Deserialize(BitStream *bs); + + /// Static version of the Deserialize() function + static bool Deserialize(char *str, BitStream *bs); + + /// Deserialize compressed string, written by SerializeCompressed + /// \param[in] bs Bitstream to serialize from + /// \param[in] readLanguageId If true, looks for the variable langaugeId in the data stream. Must match what was passed to SerializeCompressed + /// \return true if the deserialization was successful + /// \pre StringCompressor::AddReference must have been called to instantiate the class (Happens automatically from RakPeer::Startup()) + bool DeserializeCompressed(BitStream *bs, bool readLanguageId=false); + + /// Static version of the DeserializeCompressed() function + static bool DeserializeCompressed(char *str, BitStream *bs, bool readLanguageId=false); + + static const char *ToString(int64_t i); + static const char *ToString(uint64_t i); + + /// \internal + static size_t GetSizeToAllocate(size_t bytes) + { + const size_t smallStringSize = 128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2; + if (bytes<=smallStringSize) + return smallStringSize; + else + return bytes*2; + } + + /// \internal + struct SharedString + { + unsigned int refCount; + size_t bytesUsed; + char *bigString; + char *c_str; + char smallString[128-sizeof(unsigned int)-sizeof(size_t)-sizeof(char*)*2]; + }; + + /// \internal + RakString( SharedString *_sharedString ); + + /// \internal + SharedString *sharedString; + +// static SimpleMutex poolMutex; +// static DataStructures::MemoryPool pool; + /// \internal + static SharedString emptyString; + + //static SharedString *sharedStringFreeList; + //static unsigned int sharedStringFreeListAllocationCount; + /// \internal + /// List of free objects to reduce memory reallocations + static DataStructures::List freeList; + + /// Means undefined position + static unsigned int nPos; + + + static int RakStringComp( RakString const &key, RakString const &data ); + + static void LockMutex(void); + static void UnlockMutex(void); + +protected: + void Allocate(size_t len); + void Assign(const char *str); + void Clone(void); + void Free(void); + unsigned char ToLower(unsigned char c); + unsigned char ToUpper(unsigned char c); + void Realloc(SharedString *sharedString, size_t bytes); +}; + +} + +const RakNet::RakString operator+(const RakNet::RakString &lhs, const RakNet::RakString &rhs); + + +#endif diff --git a/Multiplayer/raknet/RakThread.h b/Multiplayer/raknet/RakThread.h new file mode 100644 index 000000000..0cf692c48 --- /dev/null +++ b/Multiplayer/raknet/RakThread.h @@ -0,0 +1,40 @@ +#ifndef __RAK_THREAD_H +#define __RAK_THREAD_H + +#if defined(_WIN32_WCE) +#include +#endif + +namespace RakNet +{ + +/// To define a thread, use RAK_THREAD_DECLARATION(functionName); +#if defined(_WIN32_WCE) +#define RAK_THREAD_DECLARATION(functionName) DWORD WINAPI functionName(LPVOID arguments) +#elif defined(_WIN32) +#define RAK_THREAD_DECLARATION(functionName) unsigned __stdcall functionName( void* arguments ) +#else +#define RAK_THREAD_DECLARATION(functionName) void* functionName( void* arguments ) +#endif + +class RakThread +{ +public: + + /// Create a thread, simplified to be cross platform without all the extra junk + /// To then start that thread, call RakCreateThread(functionName, arguments); + /// \param[in] start_address Function you want to call + /// \param[in] arglist Arguments to pass to the function + /// \return 0=success. >0 = error code +#if defined(_WIN32_WCE) + static int Create( LPTHREAD_START_ROUTINE start_address, void *arglist); +#elif defined(_WIN32) + static int Create( unsigned __stdcall start_address( void* ), void *arglist); +#else + static int Create( void* start_address( void* ), void *arglist); +#endif +}; + +} + +#endif diff --git a/Multiplayer/raknet/Rand.h b/Multiplayer/raknet/Rand.h new file mode 100644 index 000000000..3ec06b5a7 --- /dev/null +++ b/Multiplayer/raknet/Rand.h @@ -0,0 +1,65 @@ +/// \file +/// \brief \b [Internal] Random number generator +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + + +#ifndef __RAND_H +#define __RAND_H + +#include "Export.h" + +/// Initialise seed for Random Generator +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \param[in] seed The seed value for the random number generator. +extern void RAK_DLL_EXPORT seedMT( unsigned int seed ); + +/// \internal +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern unsigned int RAK_DLL_EXPORT reloadMT( void ); + +/// Gets a random unsigned int +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return an integer random value. +extern unsigned int RAK_DLL_EXPORT randomMT( void ); + +/// Gets a random float +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +/// \return 0 to 1.0f, inclusive +extern float RAK_DLL_EXPORT frandomMT( void ); + +/// Randomizes a buffer +/// \note not threadSafe, use an instance of RakNetRandom if necessary per thread +extern void RAK_DLL_EXPORT fillBufferMT( void *buffer, unsigned int bytes ); + +// Same thing as above functions, but not global +class RAK_DLL_EXPORT RakNetRandom +{ +public: + RakNetRandom(); + ~RakNetRandom(); + void SeedMT( unsigned int seed ); + unsigned int ReloadMT( void ); + unsigned int RandomMT( void ); + float FrandomMT( void ); + void FillBufferMT( void *buffer, unsigned int bytes ); + +protected: + unsigned int state[ 624 + 1 ]; + unsigned int *next; + int left; +}; + +#endif diff --git a/Multiplayer/raknet/ReadyEvent.h b/Multiplayer/raknet/ReadyEvent.h new file mode 100644 index 000000000..3eb11683f --- /dev/null +++ b/Multiplayer/raknet/ReadyEvent.h @@ -0,0 +1,228 @@ +/// \file +/// \brief Ready event plugin. This enables a set of systems to create a signal event, set this signal as ready or unready, and to trigger the event when all systems are ready +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __READY_EVENT_H +#define __READY_EVENT_H + +class RakPeerInterface; +#include "PluginInterface.h" +#include "DS_OrderedList.h" + +/// \defgroup READY_EVENT_GROUP ReadyEvent +/// \ingroup PLUGINS_GROUP + +/// \ingroup READY_EVENT_GROUP +/// Returns the status of a remote system when querying with ReadyEvent::GetReadyStatus +enum ReadyEventSystemStatus +{ + /// ----------- Normal states --------------- + /// The remote system is not in the wait list, and we have never gotten a ready or complete message from it. + /// This is the default state for valid events + RES_NOT_WAITING, + /// We are waiting for this remote system to call SetEvent(thisEvent,true). + RES_WAITING, + /// The remote system called SetEvent(thisEvent,true), but it still waiting for other systems before completing the ReadyEvent. + RES_READY, + /// The remote system called SetEvent(thisEvent,true), and is no longer waiting for any other systems. + /// This remote system has completed the ReadyEvent + RES_ALL_READY, + + /// Error code, we couldn't look up the system because the event was unknown + RES_UNKNOWN_EVENT, +}; + +/// Ready event plugin +/// Used to signal event completion when a set of systems all set a signal flag to true. +/// This is usually used for lobby systems, or peer to peer turn based environments. +/// The user will get ID_READY_EVENT_SET and ID_READY_EVENT_UNSET as the signal flag is set or unset +/// The user will get ID_READY_EVENT_ALL_SET when all systems are done waiting for all other systems, in which case the event is considered complete, and no longer tracked. +/// \ingroup READY_EVENT_GROUP +class ReadyEvent : public PluginInterface +{ +public: + /// Constructor + ReadyEvent(); + + /// Destructor + virtual ~ReadyEvent(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// Sets or updates the initial ready state for our local system. + /// If eventId is an unknown event the event is created. + /// If eventId was previously used and you want to reuse it, call DeleteEvent first, or else you will keep the same event signals from before + /// Systems previously or later added through AddToWaitList() with the same \a eventId when isReady=true will get ID_READY_EVENT_SET + /// Systems previously added through AddToWaitList with the same \a eventId will get ID_READY_EVENT_UNSET + /// For both ID_READY_EVENT_SET and ID_READY_EVENT_UNSET, eventId is encoded in bytes 1 through 1+sizeof(int) + /// \param[in] eventId A user-defined identifier to wait on. This can be a sequence counter, an event identifier, or anything else you want. + /// \param[in] isReady True to signal we are ready to proceed with this event, false to unsignal + /// \return True on success. False (failure) on unknown eventId + bool SetEvent(int eventId, bool isReady); + + /// When systems can call SetEvent() with isReady==false, it is possible for one system to return true from IsEventCompleted() while the other systems return false + /// This can occur if a system SetEvent() with isReady==false while the completion message is still being transmitted. + /// If your game has the situation where some action should be taken on all systems when IsEventCompleted() is true for any system, then call ForceCompletion() when the action begins. + /// This will force all systems to return true from IsEventCompleted(). + /// \param[in] eventId A user-defined identifier to immediately set as completed + bool ForceCompletion(int eventId); + + /// Deletes an event. We will no longer wait for this event, and any systems that we know have set the event will be forgotten. + /// Call this to clear memory when events are completed and you know you will never need them again. + /// \param[in] eventId A user-defined identifier + /// \return True on success. False (failure) on unknown eventId + bool DeleteEvent(int eventId); + + /// Returns what was passed to SetEvent() + /// \return The value of isReady passed to SetEvent(). Also returns false on unknown event. + bool IsEventSet(int eventId); + + /// Returns if the event is about to be ready and we are negotiating the final packets. + /// This will usually only be true for a very short time, after which IsEventCompleted should return true. + /// While this is true you cannot add to the wait list, or SetEvent() isReady to false anymore. + /// \param[in] eventId A user-defined identifier + /// \return True if any other system has completed processing. Will always be true if IsEventCompleted() is true + bool IsEventCompletionProcessing(int eventId) const; + + /// Returns if the wait list is a subset of the completion list. + /// Call this after all systems you want to wait for have been added with AddToWaitList + /// If you are waiting for a specific number of systems (such as players later connecting), also check GetRemoteWaitListSize(eventId) to be equal to 1 less than the total number of participants. + /// \param[in] eventId A user-defined identifier + /// \return True on completion. False (failure) on unknown eventId, or the set is not completed. + bool IsEventCompleted(int eventId) const; + + /// Returns if this is a known event. + /// Events may be known even if we never ourselves referenced them with SetEvent, because other systems created them via ID_READY_EVENT_SET. + /// \param[in] eventId A user-defined identifier + /// \return true if we have this event, false otherwise + bool HasEvent(int eventId); + + /// Returns the total number of events stored in the system. + /// \return The total number of events stored in the system. + unsigned GetEventListSize(void) const; + + /// Returns the event ID stored at a particular index. EventIDs are stored sorted from least to greatest. + /// \param[in] index Index into the array, from 0 to GetEventListSize() + /// \return The event ID stored at a particular index + int GetEventAtIndex(unsigned index) const; + + /// Adds a system to wait for to signal an event before considering the event complete and returning ID_READY_EVENT_ALL_SET. + /// As we add systems, if this event was previously set to true with SetEvent, these systems will get ID_READY_EVENT_SET. + /// As these systems disconnect (directly or indirectly through the router) they are removed. + /// \note If the event completion process has already started, you cannot add more systems, as this would cause the completion process to fail + /// \param[in] eventId A user-defined number previously passed to SetEvent that has not yet completed + /// \param[in] addressArray An address to wait for event replies from. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. Until all systems in this list have called SetEvent with this ID and true, and have this system in the list, we won't get ID_READY_EVENT_COMPLETE + /// \return True on success, false on unknown eventId (this should be considered an error), or if the completion process has already started. + bool AddToWaitList(int eventId, SystemAddress address); + + /// Removes systems from the wait list, which should have been previously added with AddToWaitList + /// \note Systems that directly or indirectly disconnect from us are automatically removed from the wait list + /// \param[in] address The system to remove from the wait list. Pass UNASSIGNED_SYSTEM_ADDRESS for all currently connected systems. + /// \return True on success, false on unknown eventId (this should be considered an error) + bool RemoveFromWaitList(int eventId, SystemAddress address); + + /// Returns if a particular system is waiting on a particular event. + /// \param[in] eventId A user-defined identifier + /// \param[in] The address of the system we are checking up on + /// \return True if this system is waiting on this event, false otherwise. + bool IsInWaitList(int eventId, SystemAddress address); + + /// Returns the total number of systems we are waiting on for this event. + /// Does not include yourself + /// \param[in] eventId A user-defined identifier + /// \return The total number of systems we are waiting on for this event. + unsigned GetRemoteWaitListSize(int eventId) const; + + /// Returns the system address of a system at a particular index, for this event. + /// \param[in] eventId A user-defined identifier + /// \param[in] index Index into the array, from 0 to GetWaitListSize() + /// \return The system address of a system at a particular index, for this event. + SystemAddress GetFromWaitListAtIndex(int eventId, unsigned index) const; + + /// For a remote system, find out what their ready status is (waiting, signaled, complete). + /// \param[in] eventId A user-defined identifier + /// \param[in] address Which system we are checking up on + /// \return The status of this system, for this particular event. \sa ReadyEventSystemStatus + ReadyEventSystemStatus GetReadyStatus(int eventId, SystemAddress address); + + /// This channel will be used for all RakPeer::Send calls + /// \param[in] newChannel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. + void SetSendChannel(unsigned char newChannel); + + // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- + /// \internal + /// Status of a remote system + struct RemoteSystem + { + MessageID lastSentStatus, lastReceivedStatus; + SystemAddress systemAddress; + }; + static int RemoteSystemCompBySystemAddress( const SystemAddress &key, const RemoteSystem &data ); + /// \internal + /// An event, with a set of systems we are waiting for, a set of systems that are signaled, and a set of systems with completed events + struct ReadyEventNode + { + int eventId; // Sorted on this + MessageID eventStatus; + DataStructures::OrderedList systemList; + }; + static int ReadyEventNodeComp( const int &key, ReadyEvent::ReadyEventNode * const &data ); + + +protected: + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + void OnAttach(RakPeerInterface *peer); + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + virtual void OnShutdown(RakPeerInterface *peer); + + void Clear(void); + /* + bool AnyWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersCompleted(unsigned eventIndex) const; + bool AllWaitersReady(unsigned eventIndex) const; + void SendAllReady(unsigned eventId, SystemAddress address); + void BroadcastAllReady(unsigned eventIndex); + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void BroadcastReadyUpdate(unsigned eventIndex); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + bool IsLocked(unsigned eventIndex) const; + bool IsAllReadyByIndex(unsigned eventIndex) const; + */ + + void SendReadyStateQuery(unsigned eventId, SystemAddress address); + void SendReadyUpdate(unsigned eventIndex, unsigned systemIndex, bool forceIfNotDefault); + void BroadcastReadyUpdate(unsigned eventIndex, bool forceIfNotDefault); + void RemoveFromAllLists(SystemAddress address); + void OnReadyEventQuery(RakPeerInterface *peer, Packet *packet); + void PushCompletionPacket(unsigned eventId); + bool AddToWaitListInternal(unsigned eventIndex, SystemAddress address); + void OnReadyEventForceAllSet(RakPeerInterface *peer, Packet *packet); + void OnReadyEventPacketUpdate(RakPeerInterface *peer, Packet *packet); + void UpdateReadyStatus(unsigned eventIndex); + bool IsEventCompletedByIndex(unsigned eventIndex) const; + unsigned CreateEvent(int eventId, bool isReady); + bool SetEventByIndex(int eventIndex, bool isReady); + + DataStructures::OrderedList readyEventNodeList; + unsigned char channel; + RakPeerInterface *rakPeer; +}; + +#endif diff --git a/Multiplayer/raknet/RefCountedObj.h b/Multiplayer/raknet/RefCountedObj.h new file mode 100644 index 000000000..2fa487b0b --- /dev/null +++ b/Multiplayer/raknet/RefCountedObj.h @@ -0,0 +1,34 @@ +/// \file +/// \brief \b Reference counted object. Very simple class for quick and dirty uses. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __REF_COUNTED_OBJ_H +#define __REF_COUNTED_OBJ_H + +#include "RakMemoryOverride.h" + +/// World's simplest class :) +class RefCountedObj +{ + public: + RefCountedObj() {refCount=1;} + virtual ~RefCountedObj() {} + void AddRef(void) {refCount++;} + void Deref(void) {if (--refCount==0) RakNet::OP_DELETE(this);} + int refCount; +}; + +#endif diff --git a/Multiplayer/raknet/ReliabilityLayer.h b/Multiplayer/raknet/ReliabilityLayer.h new file mode 100644 index 000000000..4dffdaa40 --- /dev/null +++ b/Multiplayer/raknet/ReliabilityLayer.h @@ -0,0 +1,378 @@ +/// \file +/// \brief \b [Internal] Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __RELIABILITY_LAYER_H +#define __RELIABILITY_LAYER_H + +#include "RakMemoryOverride.h" +#include "MTUSize.h" +#include "DS_LinkedList.h" +#include "DS_List.h" +#include "SocketLayer.h" +#include "PacketPriority.h" +#include "DS_Queue.h" +#include "BitStream.h" +#include "InternalPacket.h" +#include "DataBlockEncryptor.h" +#include "RakNetStatistics.h" +#include "SHA1.h" +#include "DS_OrderedList.h" +#include "DS_RangeList.h" +#include "DS_BPlusTree.h" +#include "DS_MemoryPool.h" + +class PluginInterface; +class RakNetRandom; + +/// Sizeof an UDP header in byte +#define UDP_HEADER_SIZE 28 + +/// Number of ordered streams available. You can use up to 32 ordered streams +#define NUMBER_OF_ORDERED_STREAMS 32 // 2^5 + +#define RESEND_TREE_ORDER 32 + +#include "BitStream.h" + +int SplitPacketIndexComp( SplitPacketIndexType const &key, InternalPacket* const &data ); +struct SplitPacketChannel// +{ + RakNetTimeNS lastUpdateTime; + DataStructures::OrderedList splitPacketList; +}; +int RAK_DLL_EXPORT SplitPacketChannelComp( SplitPacketIdType const &key, SplitPacketChannel* const &data ); + +/// Datagram reliable, ordered, unordered and sequenced sends. Flow control. Message splitting, reassembly, and coalescence. +class ReliabilityLayer// +{ +public: + + /// Constructor + ReliabilityLayer(); + + /// Destructor + ~ReliabilityLayer(); + + /// Resets the layer for reuse + void Reset( bool resetVariables ); + + ///Sets the encryption key. Doing so will activate secure connections + /// \param[in] key Byte stream for the encryption key + void SetEncryptionKey( const unsigned char *key ); + + /// Set the time, in MS, to use before considering ourselves disconnected after not being able to deliver a reliable packet + /// Default time is 10,000 or 10 seconds in release and 30,000 or 30 seconds in debug. + /// \param[in] time Time, in MS + void SetTimeoutTime( RakNetTime time ); + + /// Returns the value passed to SetTimeoutTime. or the default if it was never called + /// \param[out] the value passed to SetTimeoutTime + RakNetTime GetTimeoutTime(void); + + /// Packets are read directly from the socket layer and skip the reliability layer because unconnected players do not use the reliability layer + /// This function takes packet data after a player has been confirmed as connected. + /// \param[in] buffer The socket data + /// \param[in] length The length of the socket data + /// \param[in] systemAddress The player that this data is from + /// \param[in] messageHandlerList A list of registered plugins + /// \param[in] MTUSize maximum datagram size + /// \retval true Success + /// \retval false Modified packet + bool HandleSocketReceiveFromConnectedPlayer( const char *buffer, unsigned int length, SystemAddress systemAddress, DataStructures::List &messageHandlerList, int MTUSize ); + + /// This allocates bytes and writes a user-level message to those bytes. + /// \param[out] data The message + /// \return Returns number of BITS put into the buffer + BitSize_t Receive( unsigned char**data ); + + /// Puts data on the send queue + /// \param[in] data The data to send + /// \param[in] numberOfBitsToSend The length of \a data in bits + /// \param[in] priority The priority level for the send + /// \param[in] reliability The reliability type for the send + /// \param[in] orderingChannel 0 to 31. Specifies what channel to use, for relational ordering and sequencing of packets. + /// \param[in] makeDataCopy If true \a data will be copied. Otherwise, only a pointer will be stored. + /// \param[in] MTUSize maximum datagram size + /// \param[in] currentTime Current time, as per RakNet::GetTime() + /// \return True or false for success or failure. + bool Send( char *data, BitSize_t numberOfBitsToSend, PacketPriority priority, PacketReliability reliability, unsigned char orderingChannel, bool makeDataCopy, int MTUSize, RakNetTimeNS currentTime ); + + /// Call once per game cycle. Handles internal lists and actually does the send. + /// \param[in] s the communication end point + /// \param[in] systemAddress The Unique Player Identifier who shouldhave sent some packets + /// \param[in] MTUSize maximum datagram size + /// \param[in] time current system time + /// \param[in] maxBitsPerSecond if non-zero, enforces that outgoing bandwidth does not exceed this amount + /// \param[in] messageHandlerList A list of registered plugins + void Update( SOCKET s, SystemAddress systemAddress, int MTUSize, RakNetTimeNS time, unsigned maxBitsPerSecond, DataStructures::List &messageHandlerList, RakNetRandom *rnr, bool isPS3LobbySocket ); + + /// If Read returns -1 and this returns true then a modified packetwas detected + /// \return true when a modified packet is detected + bool IsCheater( void ) const; + + /// Were you ever unable to deliver a packet despite retries? + /// \return true means the connection has been lost. Otherwise not. + bool IsDeadConnection( void ) const; + + /// Causes IsDeadConnection to return true + void KillConnection(void); + + /// Get Statistics + /// \return A pointer to a static struct, filled out with current statistical information. + RakNetStatistics * const GetStatistics( RakNetStatistics *rns ); + + ///Are we waiting for any data to be sent out or be processed by the player? + bool IsOutgoingDataWaiting(void); + bool IsReliableOutgoingDataWaiting(void); + bool AreAcksWaiting(void); + + // Set outgoing lag and packet loss properties + void ApplyNetworkSimulator( double _maxSendBPS, RakNetTime _minExtraPing, RakNetTime _extraPingVariance ); + + /// Returns if you previously called ApplyNetworkSimulator + /// \return If you previously called ApplyNetworkSimulator + bool IsNetworkSimulatorActive( void ); + + void SetSplitMessageProgressInterval(int interval); + void SetUnreliableTimeout(RakNetTime timeoutMS); + /// Has a lot of time passed since the last ack + bool AckTimeout(RakNetTimeNS curTime); + RakNetTimeNS GetNextSendTime(void) const; + RakNetTimeNS GetTimeBetweenPackets(void) const; + RakNetTimeNS GetLastTimeBetweenPacketsDecrease(void) const; + RakNetTimeNS GetLastTimeBetweenPacketsIncrease(void) const; + RakNetTimeNS GetAckPing(void) const; + + // If true, will update time between packets quickly based on ping calculations + //void SetDoFastThroughputReactions(bool fast); + + // Encoded as numMessages[unsigned int], message1BitLength[unsigned int], message1Data (aligned), ... + //void GetUndeliveredMessages(RakNet::BitStream *messages, int MTUSize); + +private: + + /// Generates a datagram (coalesced packets) + /// \param[out] output The resulting BitStream + /// \param[in] Current MTU size + /// \param[out] reliableDataSent Set to true or false as a return value as to if reliable data was sent. + /// \param[in] time Current time + /// \param[in] systemAddress Who we are sending to + /// \param[in] messageHandlerList A list of registered plugins + /// \return If any data was sent + bool GenerateDatagram( RakNet::BitStream *output, int MTUSize, bool *reliableDataSent, RakNetTimeNS time, SystemAddress systemAddress, bool *hitMTUCap, DataStructures::List &messageHandlerList ); + + /// Send the contents of a bitstream to the socket + /// \param[in] s The socket used for sending data + /// \param[in] systemAddress The address and port to send to + /// \param[in] bitStream The data to send. + void SendBitStream( SOCKET s, SystemAddress systemAddress, RakNet::BitStream *bitStream, RakNetRandom *rnr, bool isPS3LobbySocket); + + ///Parse an internalPacket and create a bitstream to represent this data + /// \return Returns number of bits used + BitSize_t WriteToBitStreamFromInternalPacket( RakNet::BitStream *bitStream, const InternalPacket *const internalPacket, RakNetTimeNS curTime ); + + /// Parse a bitstream and create an internal packet to represent this data + InternalPacket* CreateInternalPacketFromBitStream( RakNet::BitStream *bitStream, RakNetTimeNS time ); + + /// Does what the function name says + unsigned RemovePacketFromResendListAndDeleteOlderReliableSequenced( const MessageNumberType messageNumber, RakNetTimeNS time ); + + /// Acknowledge receipt of the packet with the specified messageNumber + void SendAcknowledgementPacket( const MessageNumberType messageNumber, RakNetTimeNS time ); + + /// This will return true if we should not send at this time + bool IsSendThrottled( int MTUSize ); + + /// We lost a packet + void UpdateWindowFromPacketloss( RakNetTimeNS time ); + + /// Increase the window size + void UpdateWindowFromAck( RakNetTimeNS time ); + + /// Parse an internalPacket and figure out how many header bits would be written. Returns that number + int GetBitStreamHeaderLength( const InternalPacket *const internalPacket ); + + /// Get the SHA1 code + void GetSHA1( unsigned char * const buffer, unsigned int nbytes, char code[ SHA1_LENGTH ] ); + + /// Check the SHA1 code + bool CheckSHA1( char code[ SHA1_LENGTH ], unsigned char * const buffer, unsigned int nbytes ); + + /// Search the specified list for sequenced packets on the specified ordering channel, optionally skipping those with splitPacketId, and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::List&theList, int splitPacketId = -1 ); + + /// Search the specified list for sequenced packets with a value less than orderingIndex and delete them + void DeleteSequencedPacketsInList( unsigned char orderingChannel, DataStructures::Queue&theList ); + + /// Returns true if newPacketOrderingIndex is older than the waitingForPacketOrderingIndex + bool IsOlderOrderedPacket( OrderingIndexType newPacketOrderingIndex, OrderingIndexType waitingForPacketOrderingIndex ); + + /// Split the passed packet into chunks under MTU_SIZE bytes (including headers) and save those new chunks + void SplitPacket( InternalPacket *internalPacket, int MTUSize ); + + /// Insert a packet into the split packet list + void InsertIntoSplitPacketList( InternalPacket * internalPacket, RakNetTimeNS time ); + + /// Take all split chunks with the specified splitPacketId and try to reconstruct a packet. If we can, allocate and return it. Otherwise return 0 + InternalPacket * BuildPacketFromSplitPacketList( SplitPacketIdType splitPacketId, RakNetTimeNS time ); + + /// Delete any unreliable split packets that have long since expired + void DeleteOldUnreliableSplitPackets( RakNetTimeNS time ); + + /// Creates a copy of the specified internal packet with data copied from the original starting at dataByteOffset for dataByteLength bytes. + /// Does not copy any split data parameters as that information is always generated does not have any reason to be copied + InternalPacket * CreateInternalPacketCopy( InternalPacket *original, int dataByteOffset, int dataByteLength, RakNetTimeNS time ); + + /// Get the specified ordering list + DataStructures::LinkedList *GetOrderingListAtOrderingStream( unsigned char orderingChannel ); + + /// Add the internal packet to the ordering list in order based on order index + void AddToOrderingList( InternalPacket * internalPacket ); + + /// Inserts a packet into the resend list in order + void InsertPacketIntoResendList( InternalPacket *internalPacket, RakNetTimeNS time, bool makeCopyOfInternalPacket, bool firstResend ); + + /// Memory handling + void FreeMemory( bool freeAllImmediately ); + + /// Memory handling + void FreeThreadedMemory( void ); + + /// Memory handling + void FreeThreadSafeMemory( void ); + + // Initialize the variables + void InitializeVariables( void ); + + /// Given the current time, is this time so old that we should consider it a timeout? + bool IsExpiredTime(unsigned int input, RakNetTimeNS currentTime) const; + + // Make it so we don't do resends within a minimum threshold of time + void UpdateNextActionTime(void); + + /// Does this packet number represent a packet that was skipped (out of order?) + //unsigned int IsReceivedPacketHole(unsigned int input, RakNetTime currentTime) const; + + /// Skip an element in the received packets list + //unsigned int MakeReceivedPacketHole(unsigned int input) const; + + /// How many elements are waiting to be resent? + unsigned int GetResendListDataSize(void) const; + + /// Update all memory which is not threadsafe + void UpdateThreadedMemory(void); + + void CalculateHistogramAckSize(void); + + // Used ONLY for RELIABLE_ORDERED + // RELIABLE_SEQUENCED just returns the newest one + DataStructures::List*> orderingList; + DataStructures::Queue outputQueue; + DataStructures::RangeList acknowlegements; + int splitMessageProgressInterval; + RakNetTimeNS unreliableTimeout; + + // Resend list is a tree of packets we need to resend + DataStructures::BPlusTree resendList; + // resend Queue holds the same pointers, but in order of when to send them. nextActionTime is set to 0 when the packet is no longer needed. + DataStructures::Queue resendQueue; + + DataStructures::Queue sendPacketSet[ NUMBER_OF_PRIORITIES ]; + DataStructures::OrderedList splitPacketChannelList; + MessageNumberType sendMessageNumberIndex, internalOrderIndex; + //unsigned int windowSize; + RakNetTimeNS lastAckTime; + RakNet::BitStream updateBitStream; + OrderingIndexType waitingForOrderedPacketWriteIndex[ NUMBER_OF_ORDERED_STREAMS ], waitingForSequencedPacketWriteIndex[ NUMBER_OF_ORDERED_STREAMS ]; + + // STUFF TO NOT MUTEX HERE (called from non-conflicting threads, or value is not important) + OrderingIndexType waitingForOrderedPacketReadIndex[ NUMBER_OF_ORDERED_STREAMS ], waitingForSequencedPacketReadIndex[ NUMBER_OF_ORDERED_STREAMS ]; + bool deadConnection, cheater; + // unsigned int lastPacketSendTime,retransmittedFrames, sentPackets, sentFrames, receivedPacketsCount, bytesSent, bytesReceived,lastPacketReceivedTime; + SplitPacketIdType splitPacketId; + RakNetTime timeoutTime; // How long to wait in MS before timing someone out + //int MAX_AVERAGE_PACKETS_PER_SECOND; // Name says it all +// int RECEIVED_PACKET_LOG_LENGTH, requestedReceivedPacketLogLength; // How big the receivedPackets array is +// unsigned int *receivedPackets; + unsigned int blockWindowIncreaseUntilTime; + RakNetStatistics statistics; + + RakNetTimeNS histogramStart; + unsigned histogramBitsSent; + + + /// Memory-efficient receivedPackets algorithm: + /// receivedPacketsBaseIndex is the packet number we are expecting + /// Everything under receivedPacketsBaseIndex is a packet we already got + /// Everything over receivedPacketsBaseIndex is stored in hasReceivedPacketQueue + /// It stores the time to stop waiting for a particular packet number, where the packet number is receivedPacketsBaseIndex + the index into the queue + /// If 0, we got got that packet. Otherwise, the time to give up waiting for that packet. + /// If we get a packet number where (receivedPacketsBaseIndex-packetNumber) is less than half the range of receivedPacketsBaseIndex then it is a duplicate + /// Otherwise, it is a duplicate packet (and ignore it). + DataStructures::Queue hasReceivedPacketQueue; + MessageNumberType receivedPacketsBaseIndex; + bool resetReceivedPackets; + + RakNetTimeNS lastUpdateTime; + RakNetTimeNS timeBetweenPackets, nextSendTime, ackPing; + RakNetTimeNS ackPingSamples[256]; // Must be range of unsigned char to wrap ackPingIndex properly + RakNetTimeNS ackPingSum; + unsigned char ackPingIndex; + //RakNetTimeNS nextLowestPingReset; + RemoteSystemTimeType remoteSystemTime; + bool continuousSend; + RakNetTimeNS lastTimeBetweenPacketsIncrease,lastTimeBetweenPacketsDecrease; + // Limit changes in throughput to once per ping - otherwise even if lag starts we don't know about it + // In the meantime the connection is flooded and overrun. + RakNetTimeNS nextAllowedThroughputSample; + + // If Update::maxBitsPerSecond > 0, then throughputCapCountdown is used as a timer to prevent sends for some amount of time after each send, depending on + // the amount of data sent + long long throughputCapCountdown; + + DataBlockEncryptor encryptor; + unsigned sendPacketCount, receivePacketCount; + + ///This variable is so that free memory can be called by only the update thread so we don't have to mutex things so much + bool freeThreadedMemoryOnNextUpdate; + + // If we backoff due to packetloss, don't remeasure until all waiting resends have gone out or else we overcount + bool packetlossThisSample, backoffThisSample; + unsigned packetlossThisSampleResendCount; + RakNetTimeNS lastPacketlossTime; + + //long double timeBetweenPacketsIncreaseMultiplier, timeBetweenPacketsDecreaseMultiplier; + +#ifndef _RELEASE + struct DataAndTime// + { + char data[ MAXIMUM_MTU_SIZE ]; + unsigned int length; + RakNetTimeNS sendTime; + }; + DataStructures::List delayList; + + // Internet simulator + double maxSendBPS; + RakNetTime minExtraPing, extraPingVariance; +#endif + + // This has to be a member because it's not threadsafe when I removed the mutexes + DataStructures::MemoryPool internalPacketPool; +}; + +#endif diff --git a/Multiplayer/raknet/Replica.h b/Multiplayer/raknet/Replica.h new file mode 100644 index 000000000..af5bca21c --- /dev/null +++ b/Multiplayer/raknet/Replica.h @@ -0,0 +1,114 @@ +/// \file +/// \brief Contains interface Replica used by the ReplicaManager. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __REPLICA_H +#define __REPLICA_H + +#include "NetworkIDObject.h" +#include "PacketPriority.h" +#include "ReplicaEnums.h" + +/// This is an interface of a replicated object for use in the framework of ReplicaManager +/// You should derive from this class, implementing the functions to provide the behavior you want. +/// If your architecture doesn't allow you to derive from this class, you can store an instance of a derived instance of this class in your base game object. +/// In that case, use GetParent() and SetParent() and propagate the function calls up to your real classes. For an example where I do this, see Monster.h in the ReplicaManagerCS sample. +/// \note All send functions are called one for every target recipient, so you can customize the data sent per-user. +/// \brief The interface to derive your game's networked classes from +/// \ingroup REPLICA_MANAGER_GROUP +class Replica : public NetworkIDObject +{ +public: + /// This function is called in the first update tick after this object is first passed to ReplicaManager::Replicate for each player, and also when a new participant joins + /// The intent of this function is to tell another system to create an instance of this class. + /// You can do this however you want - I recommend having each class having a string identifier which is registered with StringTable, and then using EncodeString to write the name of the class. In the callback passed to SetReceiveConstructionCB create an instance of the object based on that string. + /// \note If you return true from IsNetworkIDAuthority, which you should do for a server or peer, I recommend encoding the value returned by GetNetworkID() into your bitstream and calling SetNetworkID with that value in your SetReceiveConstructionCB callback. + /// \note Dereplicate, SetScope, and SignalSerializeNeeded all rely on being able to call GET_OBJECT_FROM_ID which requires that SetNetworkID be called on that object. + /// \note SendConstruction is called once for every new player that connects and every existing player when an object is passed to Replicate. + /// \param[in] currentTime The current time that would be returned by RakNet::GetTime(). That's a slow call I do already, so you can use the parameter instead of having to call it yourself. + /// \param[in] systemAddress The participant to send to. + /// \param[in,out] flags Per-object per-system serialization flags modified by this function, ReplicaManager::SignalSerializationFlags, and ReplicaManager::AccessSerializationFlags. Useful for simple customization of what you serialize based on application events. This value is not automatically reset. + /// \param[out] outBitStream The data you want to write in the message. If you do not write to outBitStream and return true, then no send call will occur and the system will consider this object as not created on that remote system. + /// \param[out] includeTimestamp Set to true to include a timestamp with the message. This will be reflected in the timestamp parameter of the callback. Defaults to false. + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult SendConstruction( RakNetTime currentTime, SystemAddress systemAddress, unsigned int &flags, RakNet::BitStream *outBitStream, bool *includeTimestamp )=0; + + /// The purpose of the function is to send a packet containing the data in \a outBitStream to \a systemAddress telling that system that Dereplicate was called. + /// In the code, this is called in the update cycle after you call ReplicaManager::Destruct(). Then, if you write to outBitStream, a message is sent to that participant. + /// This is the one send you cannot delay because objects may be deleted and we can't read into them past this. + /// \param[out] outBitStream The data to send. If you do not write to outBitStream, then no send call will occur + /// \param[in] systemAddress The participant to send to. + /// \param[out] includeTimestamp Set to true to include a timestamp with the message. This will be reflected in the timestamp parameter of the callback. Defaults to false. + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult SendDestruction(RakNet::BitStream *outBitStream, SystemAddress systemAddress, bool *includeTimestamp )=0; + + /// This function is called when SendDestruction is sent from another system. Delete your object if you want. + /// \param[in] inBitStream What was sent in SendDestruction::outBitStream + /// \param[in] systemAddress The participant that sent this message to us. + /// \param[in] timestamp if Serialize::SendDestruction was set to true, the time the packet was sent. + /// \return See ReplicaReturnResult. Only REPLICA_PROCESSING_DONE is valid, and will send the destruction message. Anything else will not send any messages. + virtual ReplicaReturnResult ReceiveDestruction(RakNet::BitStream *inBitStream, SystemAddress systemAddress, RakNetTime timestamp)=0; + + /// Called when ReplicaManager::SetScope is called with a different value than what it currently has. + /// It is up to you to write \a inScope to \a outBitStream. Not doing so, and returning true, means you want to cancel the scope change call. + /// If \a inScope is true, you return true, and data is written to outBitStream, then Serialize will be called automatically + /// This is a convenience feature, since there's almost no case where an object would go in scope but not be serialized + /// \param[in] inScope The new scope that will be sent to ReceiveScopeChange that originally came from SetScope + /// \param[out] outBitStream The data to send. If you do not write to outBitStream and return true, then no send will occur and the object will keep its existing scope + /// \param[in] currentTime The current time that would be returned by RakNet::GetTime(). That's a slow call I do already, so you can use the parameter instead of having to call it yourself. + /// \param[in] systemAddress The participant to send to. + /// \param[out] includeTimestamp Set to true to include a timestamp with the message. This will be reflected in the timestamp parameter of the callback. Defaults to false. + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult SendScopeChange(bool inScope, RakNet::BitStream *outBitStream, RakNetTime currentTime, SystemAddress systemAddress, bool *includeTimestamp )=0; + + /// Called when when we get the SendScopeChange message. The new scope should have been encoded (by you) into \a inBitStream + /// \param[in] inBitStream What was sent in SendScopeChange::outBitStream + /// \param[in] systemAddress The participant that sent this message to us. + /// \param[in] timestamp if Serialize::SendScopeChange was set to true, the time the packet was sent. + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult ReceiveScopeChange(RakNet::BitStream *inBitStream,SystemAddress systemAddress, RakNetTime timestamp)=0; + + /// Called when ReplicaManager::SignalSerializeNeeded is called with this object as the parameter. + /// The system will ensure that Serialize only occurs for participants that have this object constructed and in scope + /// The intent of this function is to serialize all your class member variables for remote transmission. + /// \param[out] sendTimestamp Set to true to include a timestamp with the message. This will be reflected in the timestamp parameter Deserialize. Defaults to false. + /// \param[out] outBitStream The data you want to write in the message. If you do not write to outBitStream and return true, then no send will occur for this participant. + /// \param[in] lastSendTime The last time Serialize returned true and outBitStream was written to. 0 if this is the first time the function has ever been called for this \a systemAddress + /// \param[in] priority Passed to RakPeer::Send for the send call. + /// \param[in] reliability Passed to RakPeer::Send for the send call. + /// \param[in] currentTime The current time that would be returned by RakNet::GetTime(). That's a slow call I do already, so you can use the parameter instead of having to call it yourself. + /// \param[in] systemAddress The participant we are sending to. + /// \param[in,out] flags Per-object per-system serialization flags modified by this function, ReplicaManager::SignalSerializationFlags, and ReplicaManager::AccessSerializationFlags. Useful for simple customization of what you serialize based on application events. This value is not automatically reset. + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult Serialize(bool *sendTimestamp, RakNet::BitStream *outBitStream, RakNetTime lastSendTime, PacketPriority *priority, PacketReliability *reliability, RakNetTime currentTime, SystemAddress systemAddress, unsigned int &flags)=0; + + /// Called when another participant called Serialize with our system as the target + /// \param[in] inBitStream What was written to Serialize::outBitStream + /// \param[in] timestamp if Serialize::SendTimestamp was set to true, the time the packet was sent. + /// \param[in] lastDeserializeTime Last time you returned true from this function for this object, or 0 if never, regardless of \a systemAddress. + /// \param[in] systemAddress The participant that sent this message to us. + virtual ReplicaReturnResult Deserialize(RakNet::BitStream *inBitStream, RakNetTime timestamp, RakNetTime lastDeserializeTime, SystemAddress systemAddress )=0; + + /// Used to sort the order that commands (construct, serialize) take place in. + /// Lower sort priority commands happen before higher sort priority commands. + /// Same sort priority commands take place in random order. + /// For example, if both players and player lists are replicas, you would want to create the players before the player lists if the player lists refer to the players. + /// So you could specify the players as priority 0, and the lists as priority 1, and the players would be created and serialized first + /// \return A higher value to process later, a lower value to process sooner, the same value to process in random order. + virtual int GetSortPriority(void) const=0; +}; + +#endif diff --git a/Multiplayer/raknet/ReplicaEnums.h b/Multiplayer/raknet/ReplicaEnums.h new file mode 100644 index 000000000..503122073 --- /dev/null +++ b/Multiplayer/raknet/ReplicaEnums.h @@ -0,0 +1,52 @@ +/// \file +/// \brief Contains enumerations used by the ReplicaManager system. This file is a lightweight header, so you can include it without worrying about linking in lots of other crap +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __REPLICA_ENUMS_H +#define __REPLICA_ENUMS_H + +/// Replica interface flags, used to enable and disable function calls on the Replica object +/// Passed to ReplicaManager::EnableReplicaInterfaces and ReplicaManager::DisableReplicaInterfaces +enum +{ + REPLICA_RECEIVE_DESTRUCTION=1<<0, + REPLICA_RECEIVE_SERIALIZE=1<<1, + REPLICA_RECEIVE_SCOPE_CHANGE=1<<2, + REPLICA_SEND_CONSTRUCTION=1<<3, + REPLICA_SEND_DESTRUCTION=1<<4, + REPLICA_SEND_SCOPE_CHANGE=1<<5, + REPLICA_SEND_SERIALIZE=1<<6, + REPLICA_SET_ALL = 0xFF // Allow all of the above +}; + +enum ReplicaReturnResult +{ + /// This means call the function again later, with the same parameters + REPLICA_PROCESS_LATER, + /// This means we are done processing (the normal result to return) + REPLICA_PROCESSING_DONE, + /// This means cancel the processing - don't send any network messages and don't change the current state. + REPLICA_CANCEL_PROCESS, + /// Same as REPLICA_PROCESSING_DONE, where a message is sent, but does not clear the send bit. + /// Useful for multi-part sends with different reliability levels. + /// Only currently used by Replica::Serialize + REPLICA_PROCESS_AGAIN, + /// Only returned from the Replica::SendConstruction interface, means act as if the other system had this object but don't actually + /// Send a construction packet. This way you will still send scope and serialize packets to that system + REPLICA_PROCESS_IMPLICIT +}; + +#endif diff --git a/Multiplayer/raknet/ReplicaManager.h b/Multiplayer/raknet/ReplicaManager.h new file mode 100644 index 000000000..2e123725a --- /dev/null +++ b/Multiplayer/raknet/ReplicaManager.h @@ -0,0 +1,480 @@ +/// \file +/// \brief Contains class ReplicaManager. This system provides management for your game objects and players to make serialization, scoping, and object creation and destruction easier. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __REPLICA_MANAGER_H +#define __REPLICA_MANAGER_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "DS_OrderedList.h" +#include "PluginInterface.h" +#include "NetworkIDObject.h" +#include "DS_Queue.h" +#include "ReplicaEnums.h" + +/// Forward declarations +namespace RakNet +{ + class BitStream; +}; +class Replica; +class ReplicaManager; + +/// \defgroup REPLICA_MANAGER_GROUP ReplicaManager +/// \ingroup PLUGINS_GROUP + +/// An interface for a class that handles the construction callback from the network +/// See ReplicaManager::SetReceiveConstructionCB +/// \ingroup REPLICA_MANAGER_GROUP +class ReceiveConstructionInterface +{ +public: + ReceiveConstructionInterface() {} + virtual ~ReceiveConstructionInterface() {} + + /// Called when a network object needs to be created by the ReplicaManager class + /// \param[in] inBitStream The bitstream that was written to in Replica::SendConstruction + /// \param[in] timestamp If in Replica::SendConstruction you set sendTimestamp to true, this is the time the packet was sent. Otherwise it is 0. + /// \param[in] networkID If the remote object had an NetworkID set by the time Replica::SendConstruction was called it is here. + /// \param[in] existingNetworkObject If networkID is already in use, existingNetworkObject is the pointer to that object. If existingReplica is non-zero, you usually shouldn't create a new object, unless you are reusing networkIDs, such as having the client and server both on the same computer + /// \param[in] senderId Which SystemAddress sent this packet. + /// \param[in] caller Which instance of ReplicaManager is calling this interface + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult ReceiveConstruction(RakNet::BitStream *inBitStream, RakNetTime timestamp, NetworkID networkID, NetworkIDObject *existingObject, SystemAddress senderId, ReplicaManager *caller)=0; +}; + +/// An interface for a class that handles the call to send the download complete notification +/// See ReplicaManager::SetDownloadCompleteCB +/// \ingroup REPLICA_MANAGER_GROUP +class SendDownloadCompleteInterface +{ +public: + SendDownloadCompleteInterface() {} + virtual ~SendDownloadCompleteInterface() {} + + /// \param[out] outBitStream Write whatever you want to this bitstream. It will arrive in the receiveDownloadCompleteCB callback. + /// \param[in] currentTime The current time that would be returned by RakNet::GetTime(). That's a slow call I do already, so you can use the parameter instead of having to call it yourself. + /// \param[in] senderId Who we are sending to + /// \param[in] caller Which instance of ReplicaManager is calling this interface + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult SendDownloadComplete(RakNet::BitStream *outBitStream, RakNetTime currentTime, SystemAddress senderId, ReplicaManager *caller)=0; +}; + +/// An interface for a class that handles the call to receive the download complete notification +/// See ReplicaManager::SetDownloadCompleteCB +/// \ingroup REPLICA_MANAGER_GROUP +class ReceiveDownloadCompleteInterface +{ +public: + ReceiveDownloadCompleteInterface() {} + virtual ~ReceiveDownloadCompleteInterface() {} + + /// \param[in] inBitStream The bitstream that was written to in SendDownloadCompleteInterface::SendDownloadComplete + /// \param[in] senderId The SystemAddress of the system that send the datagram + /// \param[in] caller Which instance of ReplicaManager is calling this interface + /// \return See ReplicaReturnResult + virtual ReplicaReturnResult ReceiveDownloadComplete(RakNet::BitStream *inBitStream, SystemAddress senderId, ReplicaManager *caller)=0; +}; + +/// \deprecated Use RM2_ReplicaManager in ReplicaManager2.h +/// ReplicaManager is a system manager for your game objects that performs the following tasks: +/// 1. Tracks all locally registered game objects and players and only performs operations to and for those objects and players +/// 2. Allows you to automatically turn off unneeded local and remote functions for your game objects, thus providing convenience and security against unauthorized sends. +/// 3. Sends notifications of existing game objects to new connections, including a download complete message. +/// 4. Sends notifications of new game objects to existing players. +/// A. Serialize and scoping calls are not sent to objects that were not notified of that object. +/// B. Notification calls can be canceled on a per-object basis. Object notification sends are tracked on a per-system per-object basis. +/// 5. Configurable per-system per-object scoping. +/// A. Scoping provides a mechanism to hide and unhide remote objects without destroying the whole object, used when when entities should not be destroyed but are currently not visible to systems. +/// B. Serialize calls are not sent to hidden objects. +/// C. Scoping calls can be canceled on a per-object basis. Scope is tracked on a per-system per-object basis. +/// 6. Replicate, SetScope, SignalSerializeNeeded, and the corresponding Replica interfaces are processed in RakPeer::Receive, rather than immediately. +/// A. This allows the ReplicaManager to reorganize function calls in order by dependency. This allows out of order calls, per-object call cancellation (which also cancels dependencies), and per-object call delays (which also delays dependencies) +/// B. For example, although SetScope and SignalSerializeNeeded have a dependency on SetNetworkID(), you can still call them in the constructor and call SetNetworkID() later, as long as it happens before calling RakPeer::Receive() +/// 7. The system is fast, uses little memory, and is intentionally hands off such that it can work with any game architecture and network topology +/// +/// What the ReplicaManager system does NOT do for you +/// 1. Actually create or destroy your game objects +/// 2. Associate object destruction events with remote system disconnects. +/// 3. Set networkIDs via SetNetworkID() on newly created objects. +/// 4. Object sub-serialization. Serialize only granular on the level of entire objects. If you want to serialize part of the object, you need to set your own flags and indicate in the BitStream which parts were sent and which not. +/// \brief A management system for your game objects and players to make serialization, scoping, and object creation and destruction easier. +/// \pre You must call RakPeer::SetNetworkIDManager to use this plugin. +/// \ingroup REPLICA_MANAGER_GROUP +class RAK_DLL_EXPORT ReplicaManager : public PluginInterface +{ +public: + /// Constructor + ReplicaManager(); + + /// Destructor + virtual ~ReplicaManager(); + + /// If you think all objects should have been removed, call this to assert on any that were not. + /// Useful for debugging shutdown or restarts + void AssertReplicatedObjectsClear(void); + + /// If you think all participants should have been removed, call this to assert on any that were not. + /// Useful for debugging shutdown or restarts + void AssertParticipantsClear(void); + + /// Do or don't automatically call AddParticipant when new systems connect to us. + /// Won't add automatically add connections that already exist before this was called + /// Defaults to false + /// \param[in] autoAdd True or false, to add or not + void SetAutoParticipateNewConnections(bool autoAdd); + + /// Adds a participant to the ReplicaManager system. Only these participants get packets and we only accept ReplicaManager packets from these participants. + /// This way you can have connections that have nothing to do with your game - for example remote console logins + /// \param[in] systemAddress Which player you are referring to + /// \return True on success, false on participant already exists + bool AddParticipant(SystemAddress systemAddress); + + /// Removes a participant from the data replicator system + /// This is called automatically on ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST messages, as well as CloseConnection() calls. + /// \param[in] systemAddress Which player you are referring to + /// \return True on success, false on participant does not exist + bool RemoveParticipant(SystemAddress systemAddress); + + /// Construct the specified object on the specified system + /// Replica::SendConstruction will be called immediately, this is a change from before, because otherwise if you later send other packets that refer to this object, this object won't exist yet. + /// The other system will get Replica::ReceiveConstruction + /// If your system assigns NetworkIDs, do so before calling Replicate as the NetworkID is automatically included in the packet. + /// Replicate packets that are sent to systems that already have this NetworkID are ignored. + /// \note Objects which are replicated get exactly one call to SendConstruction for every player / object permutation. + /// \note To perform scoping and serialize updates on an object already created by another system, call Construct with \a isCopy true. + /// \note Setting \a isCopy true will consider the object created on that system without actually trying to create it. + /// \note If you don't need to send updates to other systems for this object, it is more efficient to use ReferencePointer instead. + /// \note In a client / server environment, be sure to call Construct() with isCopy true to let the ReplicaManager know that the server has this object. Otherwise you won't be able to send Scope or Serialize changes to the server. + /// \param[in] replica A pointer to your object + /// \param[in] isCopy True means that this is a copy of an object that already exists on the systems specified by \a systemAddress and \a broadcast. If true, we will consider these systems as having the object without sending a datagram to them. SendConstruction will NOT be called for objects which \a isCopy is true. + /// \param[in] systemAddress The participant to send the command to, or the one to exclude if broadcast is true. + /// \param[in] broadcast True to send to all. If systemAddress!=UNASSIGNED_SYSTEM_ADDRESS then this means send to all but that participant + void Construct(Replica *replica, bool isCopy, SystemAddress systemAddress, bool broadcast); + + /// Call this with your game objects to have them send Replica::SendDestruction. + /// This will be sent immediately to all participants that have this object. Those participants will get Replica::ReceiveDestruction + /// All pending calls for this object, for this player, are canceled. + /// Nothing is actually deleted - this just signals that the other system called this function. It is up to you to actually delete your object. + /// \pre Call Replicate with this object first. + /// \pre For the other system to get the network message, SetNetworkID on that object must have been called with the same value as GetNetworkID for this object. + /// \note Call Destruct before DereferencePointer if you plan on calling both, since Destruct will fail with no pointer reference. + /// \note Calling ( with systemAddress==UNASSIGNED_SYSTEM_ADDRESS and broadcast true is equivalent to calling DereferencePointer except that Destruct also sends the destruct packet. + /// \note It is important to call this before deleting your object. Otherwise this system will crash the next Update call. + /// \param[in] replica A pointer to your object + /// \param[in] systemAddress The participant to send the command to, or the one to exclude if broadcast is true. + /// \param[in] broadcast True to send to all systems that have the object. If systemAddress!=UNASSIGNED_SYSTEM_ADDRESS then this means send to all but that participant + void Destruct(Replica *replica, SystemAddress systemAddress, bool broadcast); + + /// This makes sure the object is tracked, so you can get calls on it. + /// This will automatically happen if you call Construct, SetScope, or SignalSerializeNeeded with \a replica + /// Otherwise you need to call this, or for security the system will ignore calls that reference this object, even if given a valid NetworkID + /// Duplicate calls are safe and are simply ignored. + /// Best place to put this is in the SetReceiveConstructionCB callback so that all your objects are registered. + /// \param[in] replica A pointer to your object + void ReferencePointer(Replica *replica); + + /// Call this before you delete \a replica. This locally removes all references to this pointer. + /// No messages are sent. + /// Best place to put this is in the destructor of \a replica + /// \param[in] replica A pointer to your object + void DereferencePointer(Replica *replica); + + /// Sets the scope of your object in relation to another participant. + /// Objects that are in-scope for that participant will send out Replica::Serialize calls. Otherwise Serialize calls are not sent. + /// Scoping is useful when you want to disable sends to an object temporarily, without deleting that object. + /// Calling this results in Replica::SendScopeChange being called on the local object and Replica::ReceiveScopeChange on the remote object if that object has been created on that remote system. + /// Your game should ensure that objects not in scope are hidden, but not deallocated, on the remote system. + /// Replica::SendScopeChange with \a inScope as true will automatically perform Replica::Serialize + /// \pre Call Replicate with this object first. + /// \pre For the other system to get the network message, that object must have an NetworkID (set by SetNetworkID()) the same as our object's NetworkID (returned from GetNetworkID()). + /// \note You can set the default scope with SetDefaultScope() + /// \note Individual objects can refuse to perform the SendScopeChange call by not writing to the output bitstream while returning true. + /// \param[in] replica An object previously registered with Replicate + /// \param[in] inScope in scope or not. + /// \param[in] systemAddress The participant to send the command to, or the one to exclude if broadcast is true. + /// \param[in] broadcast True to send to all. If systemAddress!=UNASSIGNED_SYSTEM_ADDRESS then this means send to all but that participant + void SetScope(Replica *replica, bool inScope, SystemAddress systemAddress, bool broadcast); + + /// Signal that data has changed and we need to call Serialize() on the \a replica object. + /// This will happen if the object has been registered, Replica::SendConstruction wrote to outBitStream and returned true, and the object is in scope for this player. + /// \pre Call Replicate with this object first. + /// \pre For the other system to get the network message, that object must have an NetworkID (set by SetNetworkID()) the same as our object's NetworkID (returned from GetNetworkID()). + /// \param[in] replica An object previously registered with Replicate + /// \param[in] systemAddress The participant to send the command to, or the one to exclude if broadcast is true. + /// \param[in] broadcast True to send to all. If systemAddress!=UNASSIGNED_SYSTEM_ADDRESS then this means send to all but that participant + void SignalSerializeNeeded(Replica *replica, SystemAddress systemAddress, bool broadcast); + + /// Required callback + /// Set your callback to parse requests to create new objects. Specifically, when Replica::SendConstruction is called and the networkID of the object is either unset or can't be found, this callback will get that call. + /// How do you know what object to create? It's up to you, but I suggest in Replica::SendConstruction you encode the class name. The best way to do this is with the StringTable class. + /// \note If you return true from NetworkIDManager::IsNetworkIDAuthority, which you should do for a server or peer, I recommend also encoding the value returned by GetNetworkID() within Replica::SendConstruction into that bitstream and reading it here. Then set that value in a call to SetNetworkID. Dereplicate, SetScope, and SignalSerializeNeeded all rely on being able to call GET_OBJECT_FROM_ID which requires that SetNetworkID be called on that object. + /// \param[in] ReceiveConstructionInterface An instance of a class that implements ReceiveConstructionInterface + void SetReceiveConstructionCB(ReceiveConstructionInterface *receiveConstructionInterface); + + /// Set your callbacks to be called when, after connecting to another system, you get all objects that system is going to send to you when it is done with the first iteration through the object list. + /// Optional if you want to send and receive the download complete notification + /// \param[in] sendDownloadComplete A class that implements the SendDownloadCompleteInterface interface. + /// \param[in] receiveDownloadComplete A class that implements the ReceiveDownloadCompleteInterface interface. + /// \sa SendDownloadCompleteInterface , ReceiveDownloadCompleteInterface + void SetDownloadCompleteCB( SendDownloadCompleteInterface *sendDownloadComplete, ReceiveDownloadCompleteInterface *receiveDownloadComplete ); + + /// This channel will be used for all RakPeer::Send calls + /// \param[in] channel The channel to use for internal RakPeer::Send calls from this system. Defaults to 0. + void SetSendChannel(unsigned char channel); + + /// This means automatically construct all known objects to all new participants + /// Has no effect on existing participants + /// Useful if your architecture always has all objects constructed on all systems all the time anyway, or if you want them to normally start constructed + /// Defaults to false. + /// \param[in] autoConstruct true or false, as desired. + void SetAutoConstructToNewParticipants(bool autoConstruct); + + /// Set the default scope for new objects to all players. Defaults to false, which means Serialize will not be called for new objects automatically. + /// If you set this to true, then new players will get existing objects, and new objects will be sent to existing players + /// This only applies to players that connect and objects that are replicated after this call. Existing object scopes are not affected. + /// Useful to set to true if you don't use scope, or if all objects normally start in scope + /// \param[in] scope The default scope to use. + void SetDefaultScope(bool scope); + + /// When an object goes in scope for a system, you normally want to serialize that object to that system. + /// Setting this flag to true will call Serialize for you automatically when SendScopeChange returns REPLICA_PROCESSING_DONE and the scopeTrue parameter is true + /// Defaults to false + /// \param[in] autoSerialize True or false as needed. + void SetAutoSerializeInScope(bool autoSerialize); + + /// Processes all pending commands and does sends as needed. + /// This is called automatically when RakPeerInterface::Receive is called. + /// Depending on where you call RakPeerInterface::Receive you may also wish to call this manually for better responsiveness. + /// For example, if you call RakPeerInterface::Receive at the start of each game tick, this means you would have to wait a render cycle, causing + /// \param[in] peer Pointer to a valid instance of RakPeerInterface used to perform sends + void Update(RakPeerInterface *peer); + + /// Lets you enable calling any or all of the interface functions in an instance of Replica + /// This setting is the same for all participants for this object, so if you want per-participant permissions you will need to handle that inside your implementation + /// All functions enabled by default. + /// \param[in] replica The object you are referring to + /// \param[in] interfaceFlags A bitwise-OR of REPLICA_SEND_CONSTRUCTION ... REPLICA_SET_ALL corresponding to the function of the same name + void EnableReplicaInterfaces(Replica *replica, unsigned char interfaceFlags); + + /// Lets you disable calling any or all of the interface functions in an instance of Replica + /// This setting is the same for all participants for this object, so if you want per-participant permissions you will need to handle that inside your implementation + /// All functions enabled by default. + /// \note Disabling functions is very useful for security. + /// \note For example, on the server you may wish to disable all receive functions so clients cannot change server objects. + /// \param[in] replica The object you are referring to + /// \param[in] interfaceFlags A bitwise-OR of REPLICA_SEND_CONSTRUCTION ... REPLICA_SET_ALL corresponding to the function of the same name + void DisableReplicaInterfaces(Replica *replica, unsigned char interfaceFlags); + + /// Tells us if a particular system got a SendConstruction() message from this object. e.g. does this object exist on this remote system? + /// This is set by the user when calling Replicate and sending (any) data to outBitStream in Replica::SendConstruction + /// \param[in] replica The object we are checking + /// \param[in] systemAddress The system we are checking + bool IsConstructed(Replica *replica, SystemAddress systemAddress); + + /// Tells us if a particular object is in scope for a particular system + /// This is set by the user when calling SetScope and sending (any) data to outBitstream in Replica::SendScopeChange + /// \param[in] replica The object we are checking + /// \param[in] systemAddress The system we are checking + bool IsInScope(Replica *replica, SystemAddress systemAddress); + + /// Returns how many Replica instances are registered. + /// This number goes up with each non-duplicate call to Replicate and down with each non-duplicate call to Dereplicate + /// Used for GetReplicaAtIndex if you want to perform some object on all registered Replica objects. + /// \return How many replica objects are in the list of replica objects + unsigned GetReplicaCount(void) const; + + /// Returns a previously registered Replica *, from index 0 to GetReplicaCount()-1. + /// The order that Replica * objects are returned in is arbitrary (it currently happens to be ordered by pointer address). + /// Calling Dereplicate immediately deletes the Replica * passed to it, so if you call Dereplicate while using this function + /// the array will be shifted over and the current index will now reference the next object in the array, if there was one. + /// \param[in] index An index, from 0 to GetReplicaCount()-1. + /// \return A Replica * previously passed to Construct() + Replica *GetReplicaAtIndex(unsigned index); + + /// Returns the number of unique participants added with AddParticipant + /// As these systems disconnect, they are no longer participants, so this accurately returns how many participants are using the system + /// \return The number of unique participants added with AddParticipant + unsigned GetParticipantCount(void) const; + + /// Returns a SystemAddress previously added with AddParticipant + /// \param[in] index An index, from 0 to GetParticipantCount()-1. + /// \return A SystemAddress + SystemAddress GetParticipantAtIndex(unsigned index); + + /// Returns if a participant has been added + /// \return If this participant has been added + bool HasParticipant(SystemAddress systemAddress); + + /// Each participant has a per-remote object bitfield passed to the Replica::Serialize call. + /// This function can set or unset these flags for one or more participants at the same time. + /// Flags are NOT automatically cleared on serialize. You must clear them when you want to do so. + /// \param[in] replica An object previously registered with Replicate + /// \param[in] systemAddress The participant to set the flags for + /// \param[in] broadcast True to apply to all participants. If systemAddress!=UNASSIGNED_SYSTEM_ADDRESS then this means send to all but that participant + /// \param[in] set True set the bits set in \a flags with the per-object per-system serialization flags. False to unset these bits. + /// \param[in] flags Modifier to the Per-object per-system flags sent to Replica::Serialize. See the parameter /a set + void SignalSerializationFlags(Replica *replica, SystemAddress systemAddress, bool broadcast, bool set, unsigned int flags); + + /// Each participant has a per-remote object bitfield passed to the Replica::Serialize call. + /// This function is used to read and change these flags directly for a single participant. + /// It gives more control than SignalSerializationFlags but only works for a single participant at a time. + /// \param[in] replica An object previously registered with Replicate + /// \param[in] systemAddress The participant to read/write the flags for + /// \return A pointer to the per-object per-system flags sent to Replica::Serialize. You can read or modify the flags directly with this function. This pointer is only valid until the next call to RakPeer::Receive + unsigned int* AccessSerializationFlags(Replica *replica, SystemAddress systemAddress); + + // ---------------------------- ALL INTERNAL AFTER HERE ---------------------------- + + enum + { + // Treat the object as on the remote system, and send a packet + REPLICA_EXPLICIT_CONSTRUCTION=1<<0, + // Treat the object as on the remote system, but do not send a packet. Overridden by REPLICA_EXPLICIT_CONSTRUCTION. + REPLICA_IMPLICIT_CONSTRUCTION=1<<1, + REPLICA_SCOPE_TRUE=1<<2, // Mutually exclusive REPLICA_SCOPE_FALSE + REPLICA_SCOPE_FALSE=1<<3, // Mutually exclusive REPLICA_SCOPE_TRUE + REPLICA_SERIALIZE=1<<4, + }; + + /// \internal + /// One pointer and a command to act on that pointer + struct CommandStruct + { + Replica *replica; // Pointer to an external object - not allocated here. + unsigned char command; // This is one of the enums immediately above. + unsigned int userFlags; + }; + + struct RegisteredReplica + { + Replica *replica; // Pointer to an external object - not allocated here. + RakNetTime lastDeserializeTrue; // For replicatedObjects it's the last time deserialize returned true. + unsigned char allowedInterfaces; // Replica interface flags + unsigned int referenceOrder; // The order in which we started tracking this object. Used so autoconstruction can send objects in-order + }; + + struct RemoteObject + { + Replica *replica; // Pointer to an external object - not allocated here. + bool inScope; // Is replica in scope or not? + RakNetTime lastSendTime; + unsigned int userFlags; + }; + + struct ReceivedCommand + { + SystemAddress systemAddress; + NetworkID networkID; + unsigned command; // A packetID + unsigned u1; + RakNet::BitStream *userData; + }; + + + static int RegisteredReplicaComp( Replica* const &key, const ReplicaManager::RegisteredReplica &data ); + static int RegisteredReplicaRefOrderComp( const unsigned int &key, const ReplicaManager::RegisteredReplica &data ); + static int RemoteObjectComp( Replica* const &key, const ReplicaManager::RemoteObject &data ); + static int CommandStructComp( Replica* const &key, const ReplicaManager::CommandStruct &data ); + + /// \internal + /// One remote system + struct ParticipantStruct + { + ~ParticipantStruct(); + + // The player this participant struct represents. + SystemAddress systemAddress; + + // Call sendDownloadCompleteCB when REPLICA_SEND_CONSTRUCTION is done for all objects in commandList + // This variable tracks if we did it yet or not. + bool callDownloadCompleteCB; + + // Sorted list of Replica*, sorted by pointer, along with a command to perform on that pointer. + // Ordering is just for fast lookup. + // Nothing is allocated inside this list + // DataStructures::OrderedList commandList; + // June 4, 2007 - Don't sort commands in the command list. The game replies on processing the commands in order + DataStructures::List commandList; + + // Sorted list of Replica*, sorted by pointer, along with if that object is inScope or not for this system + // Only objects that exist on the remote system are in this list, so not all objects are necessarily in this list + DataStructures::OrderedList remoteObjectList; + + // List of pending ReceivedCommand to process + DataStructures::Queue pendingCommands; + }; + + static int ParticipantStructComp( const SystemAddress &key, ReplicaManager::ParticipantStruct * const &data ); + +protected: + /// Frees all memory + void Clear(void); + // Processes a struct representing a received command + ReplicaReturnResult ProcessReceivedCommand(ParticipantStruct *participantStruct, ReceivedCommand *receivedCommand); + unsigned GetCommandListReplicaIndex(const DataStructures::List &commandList, Replica *replica, bool *objectExists) const; + + // Plugin interface functions + void OnAttach(RakPeerInterface *peer); + PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + void OnShutdown(RakPeerInterface *peer); + + /// List of objects replicated in the Replicate function. + /// Used to make sure queued actions happen on valid pointers, since objects are removed from the list in Dereplicate + /// Sorted by raw pointer address using the default sort + DataStructures::OrderedList replicatedObjects; + + /// List of participants + /// Each participant has several queues of pending commands + /// Sorted by systemAddress + /// The only complexity is that each participant also needs a list of objects that mirror the variable replicatedObjects so we know per-player if that object is in scope + DataStructures::OrderedList participantList; + + // Internal functions + ParticipantStruct* GetParticipantBySystemAddress(const SystemAddress systemAddress) const; + + // Callback pointers. + + // Required callback to handle construction calls + ReceiveConstructionInterface *_constructionCB; + + // Optional callbacks to send and receive download complete. + SendDownloadCompleteInterface *_sendDownloadCompleteCB; + ReceiveDownloadCompleteInterface *_receiveDownloadCompleteCB; + + // Channel to do send calls on. All calls are reliable ordered except for Replica::Serialize + unsigned char sendChannel; + + // Stores what you pass to SetAutoParticipateNewConnections + bool autoParticipateNewConnections; + bool autoSerializeInScope; + + bool defaultScope; + bool autoConstructToNewParticipants; + unsigned int nextReferenceIndex; + + RakPeerInterface *rakPeer; + +#ifdef _DEBUG + // Check for and assert on recursive calls to update + bool inUpdate; +#endif +}; + + +#endif diff --git a/Multiplayer/raknet/ReplicaManager2.h b/Multiplayer/raknet/ReplicaManager2.h new file mode 100644 index 000000000..fe50ff686 --- /dev/null +++ b/Multiplayer/raknet/ReplicaManager2.h @@ -0,0 +1,858 @@ +/// \file +/// \brief Contains the second iteration of the ReplicaManager class. This system automatically creates and destroys objects, downloads the world to new players, manages players, and automatically serializes as needed. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __REPLICA_MANAGER_2_H +#define __REPLICA_MANAGER_2_H + +#include "Export.h" +#include "RakNetTypes.h" +#include "DS_Map.h" +#include "PluginInterface.h" +#include "NetworkIDObject.h" +#include "PacketPriority.h" +#include "GetTime.h" +#include "BitStream.h" +#include "DS_Queue.h" + +namespace RakNet +{ +class BitStream; +class Replica2; +class Connection_RM2; +class Connection_RM2Factory; + +/// \defgroup REPLICA_MANAGER_2_GROUP ReplicaManager2 +/// \ingroup PLUGINS_GROUP + +/// \brief These are the types of events that can cause network data to be transmitted. +/// \ingroup REPLICA_MANAGER_2_GROUP +typedef int SerializationType; +enum +{ + /// Serialization command initiated by the user + SEND_SERIALIZATION_GENERIC_TO_SYSTEM, + /// Serialization command initiated by the user + BROADCAST_SERIALIZATION_GENERIC_TO_SYSTEM, + /// Serialization command automatically called after sending construction of the object + SEND_SERIALIZATION_CONSTRUCTION_TO_SYSTEM, + /// Serialization command automatically called after sending construction of the object + BROADCAST_SERIALIZATION_CONSTRUCTION_TO_SYSTEM, + /// Automatic serialization of data, based on Replica2::AddAutoSerializeTimer + SEND_AUTO_SERIALIZE_TO_SYSTEM, + /// Automatic serialization of data, based on Replica2::AddAutoSerializeTimer + BROADCAST_AUTO_SERIALIZE_TO_SYSTEM, + /// Received a serialization command, relaying to systems other than the sender + RELAY_SERIALIZATION_TO_SYSTEMS, + + /// If SetAutoAddNewConnections is true, this is the command sent when sending all game objects to new connections automatically + SEND_CONSTRUCTION_SERIALIZATION_AUTO_INITIAL_DOWNLOAD_TO_SYSTEM, + // Automatically sent message indicating if the replica is visible or not to a new connection + SEND_VISIBILITY_AUTO_INITIAL_DOWNLOAD_TO_SYSTEM, + /// The data portion of the game download, preceeded by SEND_CONSTRUCTION_SERIALIZATION_AUTO_INITIAL_DOWNLOAD_TO_SYSTEM + SEND_DATA_SERIALIZATION_AUTO_INITIAL_DOWNLOAD_TO_SYSTEM, + + /// Default reason to send a destruction command + SEND_DESTRUCTION_GENERIC_TO_SYSTEM, + /// Triggered by ReplicaManager2::RecalculateVisibility - A replica is now never constructed, so needs to be destroyed + SEND_DESTRUCTION_VISIBILITY_RECALCULATION_TO_SYSTEM, + /// Triggered by Replica2::BroadcastDestruction + BROADCAST_DESTRUCTION_GENERIC_TO_SYSTEM, + /// Received destruction message, relaying to other systems + RELAY_DESTRUCTION_TO_SYSTEMS, + + /// Default reason to send a construction command + SEND_CONSTRUCTION_GENERIC_TO_SYSTEM, + /// Triggered by ReplicaManager2::RecalculateVisibility - A replica is now always constructed, so needs to be created + SEND_CONSTRUCTION_VISIBILITY_RECALCULATION_TO_SYSTEM, + /// Triggered by Replica2::BroadcastConstruction() + BROADCAST_CONSTRUCTION_GENERIC_TO_SYSTEM, + /// Replica2::QueryIsConstructionAuthority()==false yet we called ReplicaManager2::SendConstruction() + SEND_CONSTRUCTION_REQUEST_TO_SERVER, + /// A non-authority object was created by a client, accepted, and is now relayed to all other connected systems + BROADCAST_CONSTRUCTION_REQUEST_ACCEPTED_TO_SYSTEM, + /// A non-authority object was created by a client, accepted + SEND_CONSTRUCTION_REPLY_ACCEPTED_TO_CLIENT, + /// A non-authority object was created by a client, denied + SEND_CONSTRUCTION_REPLY_DENIED_TO_CLIENT, + + /// An object is visible + SEND_VISIBILITY_TRUE_TO_SYSTEM, + /// An object is visible + BROADCAST_VISIBILITY_TRUE_TO_SYSTEM, + /// An object is not visible + SEND_VISIBILITY_FALSE_TO_SYSTEM, + /// An object is not visible + BROADCAST_VISIBILITY_FALSE_TO_SYSTEM, + /// An object is visible, and we are telling other systems about this + RELAY_VISIBILITY_TRUE_TO_SYSTEMS, + /// An object is visible, and we are telling other systems about this + RELAY_VISIBILITY_FALSE_TO_SYSTEMS, + + /// Calling Replica2::Serialize() for the purpose of reading memory to compare against later. This read will not be transmitted + AUTOSERIALIZE_RESYNCH_ONLY, + /// Calling Replica2::Serialize() to compare against a prior call. The serialization may be transmitted + AUTOSERIALIZE_DEFAULT, + + /// Start your own reasons one unit past this enum + UNDEFINED_REASON, +}; + +/// \brief A management system for your game objects and players to make serialization, scoping, and object creation and destruction easier. +/// +/// Quick start: +/// 1. Create a class that derives from Connection_RM2, implementing the Construct() function. Construct() is a factory function that should return instances of your game objects, given a user-defined identifier. +/// 2. Create a class that derives from Connection_RM2Factory, implementing AllocConnection() and DeallocConnection() to return instances of the class from step 1. +/// 3. Attach ReplicaManager2 as a plugin +/// 4. Call ReplicaManager2::SetConnectionFactory with an instance of the class from step 2. +/// 5. For each of your game classes that use this system, derive from Replica2 and implement SerializeConstruction(), Serialize(), Deserialize(). The output of SerializeConstruction() is sent to Connection_RM2::Construct() +/// 6. When these classes are allocated, call Replica2::SetReplicaManager() with the instance of ReplicaManager2 class created in step 3 (this could be done automatically in the constructor) +/// 7. Creation: Use Replica2::SendConstruction() to create the object remotely, Replica2::SendDestruction() to delete the object remotely. +/// 8. Scoping: Override Replica2::QueryVisibility() and Replica2::QueryConstruction() to return BQR_YES or BQR_NO if an object should be visible and in scope to a given connection. Defaults to BQR_ALWAYS +/// 9. Automatic serialization: Call Replica2::AddAutoSerializeTimer() to automatically call Replica2::Serialize() at intervals, compare this to the last value, and broadcast out the object when the serialized variables change. +/// +/// \pre Call RakPeer::SetNetworkIDManager() +/// \pre This system is a server or peer: Call NetworkIDManager::SetIsNetworkIDAuthority(true). +/// \pre This system is a client: Call NetworkIDManager::SetIsNetworkIDAuthority(false). +/// \pre If peer to peer, NETWORK_ID_SUPPORTS_PEER_TO_PEER should be defined in RakNetDefines.h +/// \ingroup REPLICA_MANAGER_2_GROUP +class RAK_DLL_EXPORT ReplicaManager2 : public PluginInterface +{ +public: + /// Constructor + ReplicaManager2(); + + /// Destructor + virtual ~ReplicaManager2(); + + /// Sets the factory class used to allocate connection objects + /// \param[in] factory A pointer to an instance of a class that derives from Connection_RM2Factory. This pointer it saved and not copied, so the object should remain in memory. + void SetConnectionFactory(Connection_RM2Factory *factory); + + /// \param[in] Default ordering channel to use when passing -1 to a function that takes orderingChannel as a parameter + void SetDefaultOrderingChannel(char def); + + /// \param[in] Default packet priority to use when passing NUMBER_OF_PRIORITIES to a function that takes priority as a parameter + void SetDefaultPacketPriority(PacketPriority def); + + /// \param[in] Default packet reliability to use when passing NUMBER_OF_RELIABILITIES to a function that takes reliability as a parameter + void SetDefaultPacketReliability(PacketReliability def); + + /// Auto scope will track the prior construction and serialization visibility of each registered Replica2 class, for each connection. + /// Per-tick, as the visibility or construction status of a replica changes, it will be constructed, destroyed, or the visibility will change as appropriate. + /// \param[in] construction If true, Connection_RM2::SetConstructionByReplicaQuery will be called once per PluginInterface::Update tick. This will call Replica2::QueryConstruction to return if an object should be exist on a particular connection + /// \param[in] visibility If true, Connection_RM2::SetConstructionSerializationByReplicaQuery will be called once per PluginInterface::Update tick. This will call Replica2::QuerySerialization to return if an object should be visible to a particular connection or not. + void SetAutoUpdateScope(bool construction, bool visibility); + + /// Autoadd will cause a Connection_RM2 instance to be allocated for every connection. + /// Defaults to true. Set this to false if you have connections which do not participate in the game (master server, etc). + /// \param[in] autoAdd If true, all incoming connections are added as ReplicaManager2 connections. + void SetAutoAddNewConnections(bool autoAdd); + + /// If SetAutoAddNewConnections() is false, you need to add connections manually + /// connections are also created implicitly if needed + /// \param[in] systemAddress The address of the new system + /// \return false if the connection already exists + bool AddNewConnection(SystemAddress systemAddress); + + /// Remove an existing connection. Also done automatically on ID_DISCONNECTION_NOTIFICATION and ID_CONNECTION_LOST + /// \param[in] systemAddress The address of the system to remove the connection for + /// \return false if the connection does not exist + bool RemoveConnection(SystemAddress systemAddress); + + /// Is this connection registered with the system? + /// \param[in] systemAddress The address of the system to check + /// \return true if this address is registered, false otherwise + bool HasConnection(SystemAddress systemAddress); + + /// If true, autoserialize timers added with Replica2::AddAutoSerializeTimer() will automatically decrement. + /// If false, you must call Replica2::ElapseAutoSerializeTimers() manually. + /// Defaults to true + /// \param[in] autoUpdate True to automatically call ElapseAutoSerializeTimers(). Set to false if you need to control these timers. + void SetDoReplicaAutoSerializeUpdate(bool autoUpdate); + + /// Sends a construction command to one or more systems, which will be relayed throughout the network. + /// Recipient(s) will allocate the connection via Connection_RM2Factory::AllocConnection() if it does not already exist. + /// Will trigger a call on the remote system(s) to Connection_RM2::Construct() + /// \note If using peer-to-peer, NETWORK_ID_SUPPORTS_PEER_TO_PEER should be defined in RakNetDefines.h. + /// \note This is a low level function. Beginners may wish to use Replica2::SendConstruction() or Replica2::BroadcastConstruction(). You can also override Replica2::QueryConstruction() + /// \param[in] replica The class to construct remotely + /// \param[in] replicaData User-defined serialized data representing how to construct the class. Could be the name of the class, a unique identifier, or other methods + /// \param[in] recipient Which system to send to. Use UNASSIGNED_SYSTEM_ADDRESS to send to all previously created connections. Connection_RM2Factory::AllocConnection will be called if this connection has not been previously used. + /// \param[in] timestamp Timestamp to send with the message. Use 0 to not send a timestamp if you don't need it. + /// \param[in] sendMessage True to actually send a network message. False to only register that the object exists on the remote system, useful for objects created outside ReplicaManager2, or objects that already existed in the world. + /// \param[in] exclusionList Which systems to not send to. This list is carried with the messsage, and appended to at each node in the connection graph. This is used to prevent infinite cyclical sends. + /// \param[in] localClientId If replica->QueryIsConstructionAuthority()==false, this number will be sent with SEND_CONSTRUCTION_REQUEST_TO_SERVER to the \a recipient. SEND_CONSTRUCTION_REPLY_ACCEPTED_TO_CLIENT or SEND_CONSTRUCTION_REPLY_DENIED_TO_CLIENT will be returned, and this number will be used to look up the local object in Replica2::clientPtrArray + /// \param[in] type What kind of serialization operation this is. Use one of the pre-defined types, or create your own. This will be returned in \a type in Connection_RM2::Construct() + /// \param[in] priority PacketPriority to send with. Use NUMBER_OF_PRIORITIES to use the default defined by SetDefaultPacketPriority(). + /// \param[in] reliability PacketReliability to send with. Use NUMBER_OF_RELIABILITIES to use the default defined by SetDefaultPacketReliability(); + /// \param[in] orderingChannel ordering channel to send on. Use -1 to use the default defined by SetDefaultOrderingChannel() + /// \pre Call SetConnectionFactory() with a derived instance of Connection_RM2Factory. + void SendConstruction(Replica2 *replica, BitStream *replicaData, SystemAddress recipient, RakNetTime timestamp, bool sendMessage, + DataStructures::OrderedList &exclusionList, + unsigned char localClientId, SerializationType type=SEND_CONSTRUCTION_GENERIC_TO_SYSTEM, + PacketPriority priority=NUMBER_OF_PRIORITIES, PacketReliability reliability=NUMBER_OF_RELIABILITIES, char orderingChannel=-1); + + /// Sends a destruction command to one or more systems, which will be relayed throughout the network. + /// Recipient(s) will allocate the connection via Connection_RM2Factory::AllocConnection() if it does not already exist. + /// Will trigger a call on the remote system(s) to Replica2::ReceiveDestruction() which in turn calls Replica2::DeserializeDestruction() with the value passed in \a replicaData + /// Note: This is a low level function. Beginners may wish to use Replica2::SendDestruction() or Replica2::BroadcastDestruction(). + /// \param[in] replica The class to destroy remotely + /// \param[in] replicaData User-defined serialized data. Passed to Replica2::ReceiveDestruction() + /// \param[in] recipient Which system to send to. Use UNASSIGNED_SYSTEM_ADDRESS to send to all previously created connections. Connection_RM2Factory::AllocConnection will be called if this connection has not been previously used. + /// \param[in] timestamp Timestamp to send with the message. Use 0 to not send a timestamp if you don't need it. + /// \param[in] sendMessage True to actually send a network message. False to only register that the object no longer exists on the remote system. + /// \param[in] exclusionList Which systems to not send to. This list is carried with the messsage, and appended to at each node in the connection graph. This is used to prevent infinite cyclical sends. + /// \param[in] type What kind of serialization operation this is. Use one of the pre-defined types, or create your own. This will be returned in \a type in Connection_RM2::Construct() + /// \param[in] priority PacketPriority to send with. Use NUMBER_OF_PRIORITIES to use the default defined by SetDefaultPacketPriority(). + /// \param[in] reliability PacketReliability to send with. Use NUMBER_OF_RELIABILITIES to use the default defined by SetDefaultPacketReliability(); + /// \param[in] orderingChannel ordering channel to send on. Use -1 to use the default defined by SetDefaultOrderingChannel() + /// \pre Replica::QueryIsDestructionAuthority() must return true + /// \pre Call SetConnectionFactory() with a derived instance of Connection_RM2Factory. + void SendDestruction(Replica2 *replica, BitStream *replicaData, SystemAddress recipient, RakNetTime timestamp, bool sendMessage, + DataStructures::OrderedList &exclusionList, + SerializationType type=SEND_DESTRUCTION_GENERIC_TO_SYSTEM, + PacketPriority priority=NUMBER_OF_PRIORITIES, PacketReliability reliability=NUMBER_OF_RELIABILITIES, char orderingChannel=-1); + + /// Sends a serialized object to one or more systems, which will be relayed throughout the network. + /// Recipient(s) will allocate the connection via Connection_RM2Factory::AllocConnection() if it does not already exist. + /// Will trigger a call on the remote system(s) to Replica2::ReceiveSerialization() which in turn calls Replica2::Deserialize() with the value passed in \a replicaData + /// Note: This is a low level function. Beginners may wish to use Replica2::SendSerialize() or Replica2::BroadcastSerialize(). + /// \param[in] replica The class to serialize + /// \param[in] replicaData User-defined serialized data. Passed to Replica2::ReceiveSerialization() + /// \param[in] recipient Which system to send to. Use UNASSIGNED_SYSTEM_ADDRESS to send to all previously created connections. Connection_RM2Factory::AllocConnection will be called if this connection has not been previously used. + /// \param[in] timestamp Timestamp to send with the message. Use 0 to not send a timestamp if you don't need it. + /// \param[in] exclusionList Which systems to not send to. This list is carried with the messsage, and appended to at each node in the connection graph. This is used to prevent infinite cyclical sends. + /// \param[in] type What kind of serialization operation this is. Use one of the pre-defined types, or create your own. This will be returned in \a type in Connection_RM2::Construct() + /// \param[in] priority PacketPriority to send with. Use NUMBER_OF_PRIORITIES to use the default defined by SetDefaultPacketPriority(). + /// \param[in] reliability PacketReliability to send with. Use NUMBER_OF_RELIABILITIES to use the default defined by SetDefaultPacketReliability(); + /// \param[in] orderingChannel ordering channel to send on. Use -1 to use the default defined by SetDefaultOrderingChannel() + /// \pre Replica::QueryIsSerializationAuthority() must return true + /// \pre Call SetConnectionFactory() with a derived instance of Connection_RM2Factory. + void SendSerialize(Replica2 *replica, BitStream *replicaData, SystemAddress recipient, RakNetTime timestamp, + DataStructures::OrderedList &exclusionList, + SerializationType type=SEND_SERIALIZATION_GENERIC_TO_SYSTEM, + PacketPriority priority=NUMBER_OF_PRIORITIES, PacketReliability reliability=NUMBER_OF_RELIABILITIES, char orderingChannel=-1); + + /// Sets the visibility status of an object. which will be relayed throughout the network. + /// Objects that are not visible should be hidden in the game world, and will not send AutoSerialize updates + /// Recipient(s) will allocate the connection via Connection_RM2Factory::AllocConnection() if it does not already exist. + /// Will trigger a call on the remote system(s) to Connection_RM2::ReceiveVisibility() + /// Note: This is a low level function. Beginners may wish to use Connection_RM2::SendVisibility() or override Replica2::QueryVisibility() + /// \param[in] objectList The objects to send to the system. + /// \param[in] replicaData User-defined serialized data. Read in Connection_RM2::ReceiveVisibility() + /// \param[in] recipient Which system to send to. Use UNASSIGNED_SYSTEM_ADDRESS to send to all previously created connections. Connection_RM2Factory::AllocConnection will be called if this connection has not been previously used. + /// \param[in] timestamp Timestamp to send with the message. Use 0 to not send a timestamp if you don't need it. + /// \param[in] sendMessage True to actually send a network message. False to only register that the objects exist on the remote system + /// \param[in] type What kind of serialization operation this is. Use one of the pre-defined types, or create your own. This will be returned in \a type in Connection_RM2::Construct() + /// \param[in] priority PacketPriority to send with. Use NUMBER_OF_PRIORITIES to use the default defined by SetDefaultPacketPriority(). + /// \param[in] reliability PacketReliability to send with. Use NUMBER_OF_RELIABILITIES to use the default defined by SetDefaultPacketReliability(); + /// \param[in] orderingChannel ordering channel to send on. Use -1 to use the default defined by SetDefaultOrderingChannel() + /// \pre Replica::QueryIsConstructionAuthority() must return true + /// \pre Call SetConnectionFactory() with a derived instance of Connection_RM2Factory. + void SendVisibility(Replica2 *replica, BitStream *replicaData, SystemAddress recipient, RakNetTime timestamp, + DataStructures::OrderedList &exclusionList, + SerializationType type=SEND_VISIBILITY_TRUE_TO_SYSTEM, + PacketPriority priority=NUMBER_OF_PRIORITIES, PacketReliability reliability=NUMBER_OF_RELIABILITIES, char orderingChannel=-1); + + /// Returns how many Replica2 instances are registered. + /// Replica2 instances are automatically registered when used, and unregistered when calling Deref (which is automatically done in the destructor). + /// Used for GetReplicaAtIndex if you want to perform some object on all registered Replica objects. + /// \return How many replica objects are in the list of replica objects + unsigned GetReplicaCount(void) const; + + /// Returns a previously registered Replica2 *, from index 0 to GetReplicaCount()-1. + /// Replica2* objects are returned in the order they were registered. + /// \param[in] index An index, from 0 to GetReplicaCount()-1. + /// \return A Replica2 pointer + Replica2 *GetReplicaAtIndex(unsigned index); + + /// Returns the number of registered connections. + /// Connections are registered implicitly when used. + /// Connections are unregistered on disconnect. + /// \return The number of registered connections + unsigned GetConnectionCount(void) const; + + /// Returns a connection pointer previously implicitly added. + /// \param[in] index An index, from 0 to GetConnectionCount()-1. + /// \return A Connection_RM2 pointer + Connection_RM2* GetConnectionAtIndex(unsigned index) const; + + /// Returns a connection pointer previously implicitly added. + /// \param[in] systemAddress The system address of the connection to return + /// \return A Connection_RM2 pointer + Connection_RM2* GetConnectionBySystemAddress(SystemAddress systemAddress) const; + + /// Returns the index of a connection, by SystemAddress + /// \param[in] systemAddress The system address of the connection index to return + /// \return The connection index, or -1 if no such connection + unsigned int GetConnectionIndexBySystemAddress(SystemAddress systemAddress) const; + + /// Call this when Replica2::QueryVisibility() or Replica2::QueryConstructionVisibility() changes from BQR_ALWAYS or BQR_NEVER to BQR_YES or BQR_NO + /// Otherwise these two conditions are assumed to never change + /// \param[in] Which replica to update + void RecalculateVisibility(Replica2 *replica); + + /// \internal + static int Replica2ObjectComp( RakNet::Replica2 * const &key, RakNet::Replica2 * const &data ); + /// \internal + static int Replica2CompByNetworkID( const NetworkID &key, RakNet::Replica2 * const &data ); + /// \internal + static int Connection_RM2CompBySystemAddress( const SystemAddress &key, RakNet::Connection_RM2 * const &data ); + + /// Given a replica instance, return all connections that are believed to have this replica instantiated. + /// \param[in] replica Which replica is being refered to + /// \param[out] output List of connections, ordered by system address + void GetConnectionsWithReplicaConstructed(Replica2 *replica, DataStructures::OrderedList &output); + + /// Given a replica instance, return all connections that are believed to have this replica visible + /// \param[in] replica Which replica is being refered to + /// \param[out] output List of connections, ordered by system address + void GetConnectionsWithSerializeVisibility(Replica2 *replica, DataStructures::OrderedList &output); + + /// Gets the instance of RakPeerInterface that this plugin was attached to + /// \return The instance of RakPeerInterface that this plugin was attached to + RakPeerInterface *GetRakPeer(void) const; + + /// Internally starts tracking this replica + /// \internal + void Reference(Replica2* replica, bool *newReference); + + /// Stops tracking this replica. Call before deleting the Replica. Done automatically in ~Replica() + /// \internal + void Dereference(Replica2 *replica); + +protected: + // Plugin interface functions + void OnAttach(RakPeerInterface *peer); + PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); + void OnShutdown(RakPeerInterface *peer); + void Update(RakPeerInterface *peer); + + PluginReceiveResult OnDownloadComplete(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + PluginReceiveResult OnDownloadStarted(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + PluginReceiveResult OnConstruction(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + PluginReceiveResult OnDestruction(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + PluginReceiveResult OnVisibilityChange(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + PluginReceiveResult OnSerialize(unsigned char *packetData, int packetDataLength, SystemAddress sender, RakNetTime timestamp); + + bool AddToAndWriteExclusionList(SystemAddress recipient, RakNet::BitStream *bs, DataStructures::OrderedList &exclusionList); + void WriteExclusionList(RakNet::BitStream *bs, DataStructures::OrderedList &exclusionList); + void CullByAndAddToExclusionList( + DataStructures::OrderedList &inputList, + DataStructures::OrderedList &culledOutput, + DataStructures::OrderedList &exclusionList); + void ReadExclusionList(RakNet::BitStream *bs, DataStructures::OrderedList &exclusionList); + + void Send(RakNet::BitStream *bs, SystemAddress recipient, PacketPriority priority, PacketReliability reliability, char orderingChannel); + void Clear(void); + void DownloadToNewConnection(Connection_RM2* connection, RakNetTime timestamp, PacketPriority priority, PacketReliability reliability, char orderingChannel); + + Connection_RM2* CreateConnectionIfDoesNotExist(SystemAddress systemAddress, bool *newConnection); + Connection_RM2* AutoCreateConnection(SystemAddress systemAddress, bool *newConnection); + void AddConstructionReference(Connection_RM2* connection, Replica2* replica); + void AddVisibilityReference(Connection_RM2* connection, Replica2* replica); + void RemoveVisibilityReference(Connection_RM2* connection, Replica2* replica); + void WriteHeader(RakNet::BitStream *bs, MessageID type, RakNetTime timestamp); + + friend class Connection_RM2; + friend class Replica2; + + RakPeerInterface *rakPeer; + Connection_RM2Factory *connectionFactoryInterface; + bool autoUpdateConstruction, autoUpdateVisibility; + + char defaultOrderingChannel; + PacketPriority defaultPacketPriority; + PacketReliability defaultPacketReliablity; + bool autoAddNewConnections; + bool doReplicaAutoUpdate; + RakNetTime lastUpdateTime; + + DataStructures::List fullReplicaUnorderedList; + DataStructures::OrderedList fullReplicaOrderedList; + DataStructures::OrderedList alwaysDoConstructReplicaOrderedList; + DataStructures::OrderedList alwaysDoSerializeReplicaOrderedList; + // Should only be in this list if QueryIsServer() is true + DataStructures::OrderedList variableConstructReplicaOrderedList; + DataStructures::OrderedList variableSerializeReplicaOrderedList; + + DataStructures::OrderedList connectionList; +}; + +/// \brief The result of various scope and construction queries +/// \ingroup REPLICA_MANAGER_2_GROUP +enum RAK_DLL_EXPORT BooleanQueryResult +{ + /// The query is always true, for all systems. Certain optimizations are performed here, but you should not later return any other value without first calling ReplicaManager2::RecalculateVisibility + BQR_ALWAYS, + + /// True + BQR_YES, + + /// False + BQR_NO, + + /// The query is never true, for all systems. Certain optimizations are performed here, but you should not later return any other value without first calling ReplicaManager2::RecalculateVisibility + BQR_NEVER +}; + +/// \brief Contextual information about serialization, passed to some functions in Replica2 +/// \ingroup REPLICA_MANAGER_2_GROUP +struct RAK_DLL_EXPORT SerializationContext +{ + SerializationContext() {} + ~SerializationContext() {} + SerializationContext(SerializationType st, SystemAddress relay, SystemAddress recipient, RakNetTime _timestamp) {serializationType=st; relaySourceAddress=relay; recipientAddress=recipient; timestamp=_timestamp;} + + /// The system that sent the message to us. + SystemAddress relaySourceAddress; + + /// The system that we are sending to. + SystemAddress recipientAddress; + + /// Timestamp to send with the message. 0 means undefined. Set to non-zero to actually transmit using ID_TIMESTAMP + RakNetTime timestamp; + + /// What type of serialization was performed + SerializationType serializationType; + + /// General category of serialization + static bool IsSerializationCommand(SerializationType r); + /// General category of serialization + static bool IsDownloadCommand(SerializationType r); + /// General category of serialization + static bool IsDestructionCommand(SerializationType r); + /// General category of serialization + static bool IsConstructionCommand(SerializationType r); + /// General category of serialization + static bool IsVisibilityCommand(SerializationType r); + /// General category of serialization + static bool IsVisible(SerializationType r); +}; + + +/// \brief Base class for game objects that use the ReplicaManager2 system +/// All game objects that want to use the ReplicaManager2 functionality must inherit from Replica2. +/// Generally you will want to implement at a minimum Serialize(), Deserialize(), and SerializeConstruction() +/// \ingroup REPLICA_MANAGER_2_GROUP +class RAK_DLL_EXPORT Replica2 : public NetworkIDObject +{ +public: + /// Constructor + Replica2(); + + /// Destructor + virtual ~Replica2(); + + /// Sets the replica manager to use with this Replica. + /// Will also set the NetworkIDManager associated with RakPeerInterface::SetNetworkIDManager() + /// Call this before using any of the functions below! + /// \param[in] rm A pointer to your instance of ReplicaManager 2 + void SetReplicaManager(ReplicaManager2* rm); + + /// Returns what was passed to SetReplicaManager(), or 0 if no value ever passed + /// \return Registered instance of ReplicaManager2 + ReplicaManager2* GetReplicaManager(void) const; + + /// Construct this object on other systems + /// Triggers a call to SerializeConstruction() + /// \note If using peer-to-peer, NETWORK_ID_SUPPORTS_PEER_TO_PEER should be defined in RakNetDefines.h + /// \param[in] recipientAddress Which system to send to + /// \param[in] serializationType What type of command this is. Use UNDEFINED_REASON to have a type chosen automatically + virtual void SendConstruction(SystemAddress recipientAddress, SerializationType serializationType=UNDEFINED_REASON); + + /// Destroy this object on other systems + /// Triggers a call to SerializeDestruction() + /// \param[in] recipientAddress Which system to send to + /// \param[in] serializationType What type of command this is. Use UNDEFINED_REASON to have a type chosen automatically + virtual void SendDestruction(SystemAddress recipientAddress, SerializationType serializationType=UNDEFINED_REASON); + + /// Serialize this object to another system + /// Triggers a call to Serialize() + /// \param[in] recipientAddress Which system to send to + /// \param[in] serializationType What type of command this is. Use UNDEFINED_REASON to have a type chosen automatically + virtual void SendSerialize(SystemAddress recipientAddress, SerializationType serializationType=UNDEFINED_REASON); + + /// Update the visibility of this object on another system + /// Triggers a call to SerializeVisibility() + /// \param[in] recipientAddress Which system to send to + /// \param[in] serializationType What type of command this is. Use UNDEFINED_REASON to have a type chosen automatically + virtual void SendVisibility(SystemAddress recipientAddress, SerializationType serializationType=UNDEFINED_REASON); + + /// Construct this object on other systems + /// \param[in] serializationContext Which system to send to, an input timestamp, and the SerializationType. 0 to use defaults, no timestamp. + virtual void BroadcastConstruction(SerializationContext *serializationContext=0); + + /// Serialize this object to all current connections + /// Triggers a call to SerializeConstruction() for each connection (you can serialize differently per connection). + /// \param[in] serializationContext Which system to send to, an input timestamp, and the SerializationType. 0 to use defaults, no timestamp. + virtual void BroadcastSerialize(SerializationContext *serializationContext=0); + + /// Destroy this object on all current connections + /// Triggers a call to SerializeDestruction() for each connection (you can serialize differently per connection). + /// \param[in] serializationContext Which system to send to, an input timestamp, and the SerializationType. 0 to use defaults, no timestamp. + virtual void BroadcastDestruction(SerializationContext *serializationContext=0); + + /// Update the visibility state of this object on all other systems + /// Use SEND_VISIBILITY_TRUE_TO_SYSTEM or SEND_VISIBILITY_FALSE_TO_SYSTEM in \a serializationContext::serializationType + /// Triggers a call to SerializeVisibility() for each connection (you can serialize differently per connection). + /// \param[in] serializationContext Which system to send to, an input timestamp, and the SerializationType. 0 to use defaults, no timestamp, true visibility + virtual void BroadcastVisibility(bool isVisible, SerializationContext *serializationContext=0); + + /// CALLBACK: + /// Override in order to write to \a bitStream data identifying this class for the class factory. Will be received by Connection_RM2::Construct() to create an instance of this class. + /// \param[out] bitStream Data used to identify this class, along with any data you also want to send when constructing the class + /// \param[in] serializationContext serializationType passed to Replica2::SendConstruction(), along with destination system, and a timestamp you can write to. + /// \return Return false to cancel the construction, true to process + virtual bool SerializeConstruction(RakNet::BitStream *bitStream, SerializationContext *serializationContext)=0; + + /// CALLBACK: + /// Override in order to write to \a bitStream data to send along with destruction requests. Will be received by DeserializeDestruction() + /// \param[out] bitStream Data to send + /// \param[in] serializationContext Describes which system we are sending to, and a timestamp as an out parameter + /// \return Return false to cancel the operation, true to process + virtual bool SerializeDestruction(RakNet::BitStream *bitStream, SerializationContext *serializationContext); + + /// CALLBACK: + /// Override in order to write to \a bitStream data to send as regular class serialization, for normal per-tick data. Will be received by Deserialize() + /// \param[out] bitStream Data to send + /// \param[in] serializationContext Describes which system we are sending to, and a timestamp as an out parameter + /// \return Return false to cancel the operation, true to process + virtual bool Serialize(RakNet::BitStream *bitStream, SerializationContext *serializationContext); + + /// CALLBACK: + /// Override in order to write to \a bitStream data to send along with visibility changes. Will be received by DeserializeVisibility() + /// \param[out] bitStream Data to send + /// \param[in] serializationContext Describes which system we are sending to, and a timestamp as an out parameter + /// \return Return false to cancel the operation, true to process + virtual bool SerializeVisibility(RakNet::BitStream *bitStream, SerializationContext *serializationContext); + + /// CALLBACK: + /// Receives data written by SerializeDestruction() + /// \param[in] bitStream Data sent + /// \param[in] serializationType SerializationContext::serializationType + /// \param[in] sender Which system sent this message to us + /// \param[in] timestamp If a timestamp was written, will be whatever was written adjusted to the local system time. 0 if not used. + virtual void DeserializeDestruction(RakNet::BitStream *bitStream, SerializationType serializationType, SystemAddress sender, RakNetTime timestamp); + + /// CALLBACK: + /// Receives data written by Serialize() + /// \param[in] bitStream Data sent + /// \param[in] serializationType SerializationContext::serializationType + /// \param[in] sender Which system sent this message to us + /// \param[in] timestamp If a timestamp was written, will be whatever was written adjusted to the local system time. 0 if not used. + virtual void Deserialize(RakNet::BitStream *bitStream, SerializationType serializationType, SystemAddress sender, RakNetTime timestamp); + + /// CALLBACK: + /// Receives data written by SerializeVisibility() + /// \param[in] bitStream Data sent + /// \param[in] serializationType SerializationContext::serializationType + /// \param[in] sender Which system sent this message to us + /// \param[in] timestamp If a timestamp was written, will be whatever was written adjusted to the local system time. 0 if not used. + virtual void DeserializeVisibility(RakNet::BitStream *bitStream, SerializationType serializationType, SystemAddress sender, RakNetTime timestamp); + + /// CALLBACK: + /// For a given connection, should this object exist? + /// Checked every Update cycle if ReplicaManager2::SetAutoUpdateScope() parameter \a construction is true + /// Defaults to BQR_ALWAYS + /// \param[in] connection Which connection we are referring to. 0 means unknown, in which case the system is checking for BQR_ALWAYS or BQR_NEVER as an optimization. + /// \return BQR_NO and the object will be destroyed. BQR_YES and the object will be created. BQR_ALWAYS is YES for all connections, and is optimized to only be checked once. + virtual BooleanQueryResult QueryConstruction(Connection_RM2 *connection); + + /// CALLBACK: + /// For a given connection, should this object be visible (updatable?) + /// Checked every Update cycle if ReplicaManager2::SetAutoUpdateScope() parameter \a serializationVisiblity is true + /// Defaults to BQR_ALWAYS + /// \param[in] connection Which connection we are referring to. 0 means unknown, in which case the system is checking for BQR_ALWAYS or BQR_NEVER as an optimization. + /// \return BQR_NO or BQR_YES and as this value changes per connection, you will get a call to DeserializeVisibility(). + virtual BooleanQueryResult QueryVisibility(Connection_RM2 *connection); + + /// CALLBACK: + /// Does this system have control over construction of this object? + /// While not strictly required, it is best to have this consistently return true for only one system. Otherwise systems may fight and override each other. + /// Defaults to NetworkIDManager::IsNetworkIDAuthority(); + /// \return True if an authority over this operation, for this object instance + virtual bool QueryIsConstructionAuthority(void) const; + + /// CALLBACK: + /// Does this system have control over deletion of this object? + /// While not strictly required, it is best to have this consistently return true for only one system. Otherwise systems may fight and override each other. + /// Defaults to NetworkIDManager::IsNetworkIDAuthority(); + /// \return True if an authority over this operation, for this object instance + virtual bool QueryIsDestructionAuthority(void) const; + + /// CALLBACK: + /// Does this system have control over visibility of this object? + /// While not strictly required, it is best to have this consistently return true for only one system. Otherwise systems may fight and override each other. + /// Defaults to NetworkIDManager::IsNetworkIDAuthority(); + /// \return True if an authority over this operation, for this object instance + virtual bool QueryIsVisibilityAuthority(void) const; + + /// CALLBACK: + /// Does this system have control over serialization of object members of this object? + /// It is reasonable to have this be true for more than one system, but you would want to serialize different variables so those systems do not conflict. + /// Defaults to NetworkIDManager::IsNetworkIDAuthority(); + /// \return True if an authority over this operation, for this object instance + virtual bool QueryIsSerializationAuthority(void) const; + + /// CALLBACK: + /// If QueryIsConstructionAuthority() is false for a remote system, should that system be able to create this kind of object? + /// \param[in] sender Which system sent this message to us? Also happens to be the system that is requesting to create an object + /// \param[in] replicaData Construction data used to create this object + /// \param[in] type Which type of serialization operation was performed + /// \param[in] timestamp Written timestamp with the packet. 0 if not used. + /// \return True to allow remote construction of this object. If true, we will reply with SEND_CONSTRUCTION_REPLY_ACCEPTED_TO_CLIENT and the network ID will be set on the requester. + virtual bool AllowRemoteConstruction(SystemAddress sender, RakNet::BitStream *replicaData, SerializationType type, RakNetTime timestamp); + + /// Adds a timer that will elapse every \a countdown milliseconds, calling Serialize with AUTOSERIALIZE_DEFAULT or whatever value was passed to \a serializationType + /// Every time this timer elapses, the value returned from Serialize() will be compared to the last value returned by Serialize(). + /// If different, SendSerialize() will be called automatically. + /// It is possible to create your own AUTOSERIALIZE enumerations and thus control which parts of the object is serialized + /// Use CancelAutoSerializeTimer() or ClearAutoSerializeTimers() to stop the timer. + /// If this timer already exists, it will simply override the existing countdown + /// This timer will automatically repeat every \a countdown milliseconds + /// \param[in] interval Time in milliseconds between autoserialize ticks. Use 0 to process immediately, and every tick + /// \param[in] serializationType User-defined identifier for what type of serialization operation to perform. Returned in Deserialize() as the \a serializationType parameter. + /// \param[in] countdown Amount of time before doing the next autoserialize. Defaults to interval + virtual void AddAutoSerializeTimer(RakNetTime interval, SerializationType serializationType=AUTOSERIALIZE_DEFAULT, RakNetTime countdown=(RakNetTime)-1 ); + + /// Elapse time for all timers added with AddAutoSerializeTimer() + /// Only necessary to call this if you call Replica2::SetDoReplicaAutoSerializeUpdate(false) (which defaults to true) + /// \param[in] timeElapsed How many milliseconds have elapsed since the last call + /// \param[in] resynchOnly True to only update what was considered the last send, without actually doing a send. + virtual void ElapseAutoSerializeTimers(RakNetTime timeElapsed, bool resynchOnly); + + /// Returns how many milliseconds are remaining until the next autoserialize update + /// \param[in] serializationType User-defined identifier for what type of serialization operation to perform. Returned in Deserialize() as the \a serializationType parameter. + /// \return How many milliseconds are remaining until the next autoserialize update. Returns -1 if no such autoserialization timer is in place. + RakNetTime GetTimeToNextAutoSerialize(SerializationType serializationType=AUTOSERIALIZE_DEFAULT); + + /// Do the actual send call when needed to support autoSerialize + /// If you want to do different types of send calls (UNRELIABLE for example) override this function. + /// \param[in] serializationContext Describes the recipient, sender. serializationContext::timestamp is an [out] parameter which if you write to, will be send along with the message + /// \param[in] serializedObject Data to pass to ReplicaManager2::SendSerialize() + virtual void BroadcastAutoSerialize(SerializationContext *serializationContext, RakNet::BitStream *serializedObject); + + /// Stop calling an autoSerialize timer previously setup with AddAutoSerializeTimer() + /// \param[in] serializationType Corresponding value passed to serializationType + virtual void CancelAutoSerializeTimer(SerializationType serializationType=AUTOSERIALIZE_DEFAULT); + + /// Remove and deallocate all previously added autoSerialize timers + virtual void ClearAutoSerializeTimers(void); + + /// A timer has elapsed. Compare the last value sent to the current value, and if different, send the new value + /// \internal + virtual void OnAutoSerializeTimerElapsed(SerializationType serializationType, RakNet::BitStream *output, RakNet::BitStream *lastOutput, RakNetTime lastAutoSerializeCountdown, bool resynchOnly); + + /// Immediately elapse all autoserialize timers + /// Used internally when a Deserialize() event occurs, so that the deserialize does not trigger an autoserialize itself + /// \internal + /// \param[in] resynchOnly If true, do not send a Serialize() message if the data has changed + virtual void ForceElapseAllAutoserializeTimers(bool resynchOnly); + + /// A call to Connection_RM2 Construct() has completed and the object is now internally referenced + /// \param[in] replicaData Whatever was written \a bitStream in Replica2::SerializeConstruction() + /// \param[in] type Whatever was written \a serializationType in Replica2::SerializeConstruction() + /// \param[in] replicaManager ReplicaManager2 instance that created this class. + /// \param[in] timestamp timestamp sent with Replica2::SerializeConstruction(), 0 for none. + /// \param[in] networkId NetworkID that will be assigned automatically to the new object after this function returns + /// \param[in] networkIDCollision True if the network ID that should be assigned to this object is already in use. Usuallly this is because the object already exists, and you should just read your data and return 0. + /// \return Return 0 to signal that construction failed or was refused for this object. Otherwise return the object that was created. A reference will be held to this object, and SetNetworkID() and SetReplicaManager() will be called automatically. + virtual void OnConstructionComplete(RakNet::BitStream *replicaData, SystemAddress sender, SerializationType type, ReplicaManager2 *replicaManager, RakNetTime timestamp, NetworkID networkId, bool networkIDCollision); + +protected: + + virtual void ReceiveSerialize(SystemAddress sender, RakNet::BitStream *serializedObject, SerializationType serializationType, RakNetTime timestamp, DataStructures::OrderedList &exclusionList ); + virtual void ReceiveDestruction(SystemAddress sender, RakNet::BitStream *serializedObject, SerializationType serializationType, RakNetTime timestamp, DataStructures::OrderedList &exclusionList ); + virtual void DeleteOnReceiveDestruction(SystemAddress sender, RakNet::BitStream *serializedObject, SerializationType serializationType, RakNetTime timestamp, DataStructures::OrderedList &exclusionList ); + virtual void ReceiveVisibility(SystemAddress sender, RakNet::BitStream *serializedObject, SerializationType serializationType, RakNetTime timestamp, DataStructures::OrderedList &exclusionList); + virtual Replica2 * ReceiveConstructionReply(SystemAddress sender, BitStream *replicaData, bool constructionAllowed); + + friend class ReplicaManager2; + friend class Connection_RM2; + + static unsigned char clientSharedID; + static Replica2* clientPtrArray[256]; + + bool hasClientID; + unsigned char clientID; + ReplicaManager2 *rm2; + + struct AutoSerializeEvent + { + SerializationType serializationType; + RakNetTime initialCountdown; + RakNetTime remainingCountdown; + bool writeToResult1; + RakNet::BitStream lastAutoSerializeResult1; + RakNet::BitStream lastAutoSerializeResult2; + }; + + DataStructures::Map autoSerializeTimers; + +}; + +/// \brief Implement this factory class to return instances of your Connection_RM2 derived object. This is used as a class factory and exposes functionality related to the connection and the system +/// \ingroup REPLICA_MANAGER_2_GROUP +class RAK_DLL_EXPORT Connection_RM2Factory +{ +public: + Connection_RM2Factory() {} + virtual ~Connection_RM2Factory() {} + virtual Connection_RM2* AllocConnection(void) const=0; + virtual void DeallocConnection(Connection_RM2* s) const=0; +}; + +/// \brief This class represents a connection between two instances of ReplicaManager2 +/// Represents a connection. Allocated by user supplied factory interface Connection_RM2Factory +/// Implicitly created as needed +/// Generally you will want to implement at a minimum the Construct() function, used as a factory function to create your game objects +/// \ingroup REPLICA_MANAGER_2_GROUP +class RAK_DLL_EXPORT Connection_RM2 +{ +public: + /// Constructor + Connection_RM2(); + + /// Destructor + virtual ~Connection_RM2(); + + /// Factory function, used to create instances of your game objects + /// Encoding is entirely up to you. \a replicaData will hold whatever was written \a bitStream in Replica2::SerializeConstruction() + /// One efficient way to do it is to use StringTable.h. This allows you to send predetermined strings over the network at a cost of 9 bits, up to 65536 strings + /// \note The object is not yet referenced by ReplicaManager2 in this callback. Use Replica2::OnConstructionComplete() to perform functionality such as AutoSerialize() + /// \param[in] replicaData Whatever was written \a bitStream in Replica2::SerializeConstruction() + /// \param[in] type Whatever was written \a serializationType in Replica2::SerializeConstruction() + /// \param[in] replicaManager ReplicaManager2 instance that created this class. + /// \param[in] timestamp timestamp sent with Replica2::SerializeConstruction(), 0 for none. + /// \param[in] networkId NetworkID that will be assigned automatically to the new object after this function returns + /// \param[in] networkIDCollision True if the network ID that should be assigned to this object is already in use. Usuallly this is because the object already exists, and you should just read your data and return 0. + /// \return Return 0 to signal that construction failed or was refused for this object. Otherwise return the object that was created. A reference will be held to this object, and SetNetworkID() and SetReplicaManager() will be called automatically. + virtual Replica2* Construct(RakNet::BitStream *replicaData, SystemAddress sender, SerializationType type, ReplicaManager2 *replicaManager, RakNetTime timestamp, NetworkID networkId, bool networkIDCollision)=0; + + /// CALLBACK: + /// Called before a download is sent to a new connection, called after ID_REPLICA_MANAGER_DOWNLOAD_STARTED is sent. + /// Gives you control over the list of objects to be downloaded. For greater control, you can override ReplicaManager2::DownloadToNewConnection + /// Defaults to send everything in the default order + /// \param[in] fullReplicaUnorderedList The list of all known objects in the order they were originally known about by the system (the first time used by any function) + /// \param[out] orderedDownloadList An empty list. Copy fullReplicaUnorderedList to this list to send everything. Leave elements out to not send them. Add them to the list in a different order to send them in that order. + virtual void SortInitialDownload( const DataStructures::List &orderedDownloadList, DataStructures::List &initialDownloadList ); + + + /// CALLBACK: + /// Called before a download is sent to a new connection + /// \param[out] objectData What data you want to send to DeSerializeDownloadStarted() + /// \param[in] replicaManager Which replica manager to use to perform the send + /// \param[in/out] serializationContext Target recipient, optional timestamp, type of command + virtual void SerializeDownloadStarted(RakNet::BitStream *objectData, ReplicaManager2 *replicaManager, SerializationContext *serializationContext); + + /// CALLBACK: + /// Called after a download is sent to a new connection + /// \param[out] objectData What data you want to send to DeSerializeDownloadComplete() + /// \param[in] replicaManager Which replica manager to use to perform the send + /// \param[in/out] serializationContext Target recipient, optional timestamp, type of command + virtual void SerializeDownloadComplete(RakNet::BitStream *objectData, ReplicaManager2 *replicaManager, SerializationContext *serializationContext); + + /// CALLBACK: + /// A new connection was added. All objects that are constructed and visible for this system will arrive immediately after this message. + /// Write data to \a objectData by deriving from SerializeDownloadStarted() + /// \note Only called if SetAutoUpdateScope is called with serializationVisiblity or construction true. (This is the default) + /// \param[in] objectData objectData Data written through SerializeDownloadStarted() + /// \param[in] replicaManager Which replica manager to use to perform the send + /// \param[in] timestamp timestamp sent, 0 for none + /// \param[in] serializationType Type of command + virtual void DeserializeDownloadStarted(RakNet::BitStream *objectData, SystemAddress sender, ReplicaManager2 *replicaManager, RakNetTime timestamp, SerializationType serializationType); + + /// CALLBACK: + /// A new connection was added. All objects that are constructed and visible for this system have now arrived. + /// Write data to \a objectData by deriving from SerializeDownloadComplete + /// \note Only called if SetAutoUpdateScope is called with serializationVisiblity or construction true. (This is the default) + /// \param[in] objectData objectData Data written through SerializeDownloadComplete() + /// \param[in] replicaManager Which replica manager to use to perform the send + /// \param[in] timestamp timestamp sent, 0 for none + /// \param[in] serializationType Type of command + virtual void DeserializeDownloadComplete(RakNet::BitStream *objectData, SystemAddress sender, ReplicaManager2 *replicaManager, RakNetTime timestamp, SerializationType serializationType); + + /// Given a list of objects, compare it against lastConstructionList. + /// BroadcastConstruct() is called for objects that only exist in the new list. + /// BroadcastDestruct() is called for objects that only exist in the old list. + /// This is used by SetConstructionByReplicaQuery() for all Replica2 that do not return BQR_ALWAYS from Replica2::QueryConstruction() + /// If you want to pass your own, more efficient list to check against, call ReplicaManager2::SetAutoUpdateScope with construction=false and call this function yourself when desired + /// \param[in] List of all objects that do not return BQR_ALWAYS from Replica2::QueryConstruction() that should currently be created on this system + /// \param[in] replicaManager Which replica manager to use to perform the send + virtual void SetConstructionByList(DataStructures::OrderedList ¤tVisibility, ReplicaManager2 *replicaManager); + + /// Given a list of objects, compare it against lastSerializationList. + /// Replica2::BroadcastVisibility(true) is called for objects that only exist in the new list. + /// Replica2::BroadcastVisibility(false) is called for objects that only exist in the old list. + /// This is used by SetVisibilityByReplicaQuery() for all Replica2 that do not return BQR_ALWAYS from Replica2::QueryVisibility() + /// If you want to pass your own, more efficient list to check against, call ReplicaManager2::SetAutoUpdateScope with construction=false and call this function yourself when desired + /// \param[in] List of all objects that do not return BQR_ALWAYS from Replica2::QueryConstruction() that should currently be created on this system + /// \param[in] replicaManager Which replica manager to use to perform the send + virtual void SetVisibilityByList(DataStructures::OrderedList ¤tVisibility, ReplicaManager2 *replicaManager); + + /// Go through all registered Replica2 objects that do not return BQR_ALWAYS from Replica2::QueryConstruction() + /// For each of these objects that return BQR_YES, pass them to currentVisibility in SetConstructionByList() + /// Automatically called every tick if ReplicaManager2::SetAutoUpdateScope with construction=true is called (which is the default) + /// \param[in] replicaManager Which replica manager to use to perform the send + virtual void SetConstructionByReplicaQuery(ReplicaManager2 *replicaManager); + + /// Go through all registered Replica2 objects that do not return BQR_ALWAYS from Replica2::QueryVisibility() + /// For each of these objects that return BQR_YES, pass them to currentVisibility in SetVisibilityByList() + /// Automatically called every tick if ReplicaManager2::SetAutoUpdateScope with construction=true is called (which is the default) + /// \param[in] replicaManager Which replica manager to use to perform the send + virtual void SetVisibilityByReplicaQuery(ReplicaManager2 *replicaManager); + + /// Set the system address to use with this class instance. This is set internally when the object is created + void SetSystemAddress(SystemAddress sa); + + /// Get the system address associated with this class instance. + SystemAddress GetSystemAddress(void) const; + + /// Set the guid to use with this class instance. This is set internally when the object is created + void SetGuid(RakNetGUID guid); + + /// Get the guid associated with this class instance. + RakNetGUID GetGuid(void) const; + +protected: + void Deref(Replica2* replica); + + void CalculateListExclusivity( + const DataStructures::OrderedList &listOne, + const DataStructures::OrderedList &listTwo, + DataStructures::OrderedList &exclusiveToListOne, + DataStructures::OrderedList &exclusiveToListTwo + ) const; + + virtual Replica2 * ReceiveConstruct(RakNet::BitStream *replicaData, NetworkID networkId, SystemAddress sender, unsigned char localClientId, SerializationType type, + ReplicaManager2 *replicaManager, RakNetTime timestamp, DataStructures::OrderedList &exclusionList); + + friend class ReplicaManager2; + + // Address of this participant + SystemAddress systemAddress; + RakNetGUID rakNetGuid; + + DataStructures::OrderedList lastConstructionList; + DataStructures::OrderedList lastSerializationList; +}; + +} + +#endif diff --git a/Multiplayer/raknet/Rijndael-Boxes.h b/Multiplayer/raknet/Rijndael-Boxes.h new file mode 100644 index 000000000..fedd5bd5d --- /dev/null +++ b/Multiplayer/raknet/Rijndael-Boxes.h @@ -0,0 +1,949 @@ +word8 Logtable[256] = { + 0, 0, 25, 1, 50, 2, 26, 198, 75, 199, 27, 104, 51, 238, 223, 3, +100, 4, 224, 14, 52, 141, 129, 239, 76, 113, 8, 200, 248, 105, 28, 193, +125, 194, 29, 181, 249, 185, 39, 106, 77, 228, 166, 114, 154, 201, 9, 120, +101, 47, 138, 5, 33, 15, 225, 36, 18, 240, 130, 69, 53, 147, 218, 142, +150, 143, 219, 189, 54, 208, 206, 148, 19, 92, 210, 241, 64, 70, 131, 56, +102, 221, 253, 48, 191, 6, 139, 98, 179, 37, 226, 152, 34, 136, 145, 16, +126, 110, 72, 195, 163, 182, 30, 66, 58, 107, 40, 84, 250, 133, 61, 186, + 43, 121, 10, 21, 155, 159, 94, 202, 78, 212, 172, 229, 243, 115, 167, 87, +175, 88, 168, 80, 244, 234, 214, 116, 79, 174, 233, 213, 231, 230, 173, 232, + 44, 215, 117, 122, 235, 22, 11, 245, 89, 203, 95, 176, 156, 169, 81, 160, +127, 12, 246, 111, 23, 196, 73, 236, 216, 67, 31, 45, 164, 118, 123, 183, +204, 187, 62, 90, 251, 96, 177, 134, 59, 82, 161, 108, 170, 85, 41, 157, +151, 178, 135, 144, 97, 190, 220, 252, 188, 149, 207, 205, 55, 63, 91, 209, + 83, 57, 132, 60, 65, 162, 109, 71, 20, 42, 158, 93, 86, 242, 211, 171, + 68, 17, 146, 217, 35, 32, 46, 137, 180, 124, 184, 38, 119, 153, 227, 165, +103, 74, 237, 222, 197, 49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7 +}; + +word8 Alogtable[256] = { + 1, 3, 5, 15, 17, 51, 85, 255, 26, 46, 114, 150, 161, 248, 19, 53, + 95, 225, 56, 72, 216, 115, 149, 164, 247, 2, 6, 10, 30, 34, 102, 170, +229, 52, 92, 228, 55, 89, 235, 38, 106, 190, 217, 112, 144, 171, 230, 49, + 83, 245, 4, 12, 20, 60, 68, 204, 79, 209, 104, 184, 211, 110, 178, 205, + 76, 212, 103, 169, 224, 59, 77, 215, 98, 166, 241, 8, 24, 40, 120, 136, +131, 158, 185, 208, 107, 189, 220, 127, 129, 152, 179, 206, 73, 219, 118, 154, +181, 196, 87, 249, 16, 48, 80, 240, 11, 29, 39, 105, 187, 214, 97, 163, +254, 25, 43, 125, 135, 146, 173, 236, 47, 113, 147, 174, 233, 32, 96, 160, +251, 22, 58, 78, 210, 109, 183, 194, 93, 231, 50, 86, 250, 21, 63, 65, +195, 94, 226, 61, 71, 201, 64, 192, 91, 237, 44, 116, 156, 191, 218, 117, +159, 186, 213, 100, 172, 239, 42, 126, 130, 157, 188, 223, 122, 142, 137, 128, +155, 182, 193, 88, 232, 35, 101, 175, 234, 37, 111, 177, 200, 67, 197, 84, +252, 31, 33, 99, 165, 244, 7, 9, 27, 45, 119, 153, 176, 203, 70, 202, + 69, 207, 74, 222, 121, 139, 134, 145, 168, 227, 62, 66, 198, 81, 243, 14, + 18, 54, 90, 238, 41, 123, 141, 140, 143, 138, 133, 148, 167, 242, 13, 23, + 57, 75, 221, 124, 132, 151, 162, 253, 28, 36, 108, 180, 199, 82, 246, 1 +}; + +word8 S[256] = { + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, +202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, +183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, + 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, + 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, + 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, +208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, + 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, +205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, + 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, +224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, +231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, +186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, +112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, +225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, +140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22 +}; + +word8 Si[256] = { + 82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 251, +124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 233, 203, + 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250, 195, 78, + 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139, 209, 37, +114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 101, 182, 146, +108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 167, 141, 157, 132, +144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 5, 184, 179, 69, 6, +208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3, 1, 19, 138, 107, + 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206, 240, 180, 230, 115, +150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55, 232, 28, 117, 223, 110, + 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 98, 14, 170, 24, 190, 27, +252, 86, 62, 75, 198, 210, 121, 32, 154, 219, 192, 254, 120, 205, 90, 244, + 31, 221, 168, 51, 136, 7, 199, 49, 177, 18, 16, 89, 39, 128, 236, 95, + 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 122, 159, 147, 201, 156, 239, +160, 224, 59, 77, 174, 42, 245, 176, 200, 235, 187, 60, 131, 83, 153, 97, + 23, 43, 4, 126, 186, 119, 214, 38, 225, 105, 20, 99, 85, 33, 12, 125 +}; + +word8 T1[256][4] = { +{0xc6,0x63,0x63,0xa5}, {0xf8,0x7c,0x7c,0x84}, {0xee,0x77,0x77,0x99}, {0xf6,0x7b,0x7b,0x8d}, +{0xff,0xf2,0xf2,0x0d}, {0xd6,0x6b,0x6b,0xbd}, {0xde,0x6f,0x6f,0xb1}, {0x91,0xc5,0xc5,0x54}, +{0x60,0x30,0x30,0x50}, {0x02,0x01,0x01,0x03}, {0xce,0x67,0x67,0xa9}, {0x56,0x2b,0x2b,0x7d}, +{0xe7,0xfe,0xfe,0x19}, {0xb5,0xd7,0xd7,0x62}, {0x4d,0xab,0xab,0xe6}, {0xec,0x76,0x76,0x9a}, +{0x8f,0xca,0xca,0x45}, {0x1f,0x82,0x82,0x9d}, {0x89,0xc9,0xc9,0x40}, {0xfa,0x7d,0x7d,0x87}, +{0xef,0xfa,0xfa,0x15}, {0xb2,0x59,0x59,0xeb}, {0x8e,0x47,0x47,0xc9}, {0xfb,0xf0,0xf0,0x0b}, +{0x41,0xad,0xad,0xec}, {0xb3,0xd4,0xd4,0x67}, {0x5f,0xa2,0xa2,0xfd}, {0x45,0xaf,0xaf,0xea}, +{0x23,0x9c,0x9c,0xbf}, {0x53,0xa4,0xa4,0xf7}, {0xe4,0x72,0x72,0x96}, {0x9b,0xc0,0xc0,0x5b}, +{0x75,0xb7,0xb7,0xc2}, {0xe1,0xfd,0xfd,0x1c}, {0x3d,0x93,0x93,0xae}, {0x4c,0x26,0x26,0x6a}, +{0x6c,0x36,0x36,0x5a}, {0x7e,0x3f,0x3f,0x41}, {0xf5,0xf7,0xf7,0x02}, {0x83,0xcc,0xcc,0x4f}, +{0x68,0x34,0x34,0x5c}, {0x51,0xa5,0xa5,0xf4}, {0xd1,0xe5,0xe5,0x34}, {0xf9,0xf1,0xf1,0x08}, +{0xe2,0x71,0x71,0x93}, {0xab,0xd8,0xd8,0x73}, {0x62,0x31,0x31,0x53}, {0x2a,0x15,0x15,0x3f}, +{0x08,0x04,0x04,0x0c}, {0x95,0xc7,0xc7,0x52}, {0x46,0x23,0x23,0x65}, {0x9d,0xc3,0xc3,0x5e}, +{0x30,0x18,0x18,0x28}, {0x37,0x96,0x96,0xa1}, {0x0a,0x05,0x05,0x0f}, {0x2f,0x9a,0x9a,0xb5}, +{0x0e,0x07,0x07,0x09}, {0x24,0x12,0x12,0x36}, {0x1b,0x80,0x80,0x9b}, {0xdf,0xe2,0xe2,0x3d}, +{0xcd,0xeb,0xeb,0x26}, {0x4e,0x27,0x27,0x69}, {0x7f,0xb2,0xb2,0xcd}, {0xea,0x75,0x75,0x9f}, +{0x12,0x09,0x09,0x1b}, {0x1d,0x83,0x83,0x9e}, {0x58,0x2c,0x2c,0x74}, {0x34,0x1a,0x1a,0x2e}, +{0x36,0x1b,0x1b,0x2d}, {0xdc,0x6e,0x6e,0xb2}, {0xb4,0x5a,0x5a,0xee}, {0x5b,0xa0,0xa0,0xfb}, +{0xa4,0x52,0x52,0xf6}, {0x76,0x3b,0x3b,0x4d}, {0xb7,0xd6,0xd6,0x61}, {0x7d,0xb3,0xb3,0xce}, +{0x52,0x29,0x29,0x7b}, {0xdd,0xe3,0xe3,0x3e}, {0x5e,0x2f,0x2f,0x71}, {0x13,0x84,0x84,0x97}, +{0xa6,0x53,0x53,0xf5}, {0xb9,0xd1,0xd1,0x68}, {0x00,0x00,0x00,0x00}, {0xc1,0xed,0xed,0x2c}, +{0x40,0x20,0x20,0x60}, {0xe3,0xfc,0xfc,0x1f}, {0x79,0xb1,0xb1,0xc8}, {0xb6,0x5b,0x5b,0xed}, +{0xd4,0x6a,0x6a,0xbe}, {0x8d,0xcb,0xcb,0x46}, {0x67,0xbe,0xbe,0xd9}, {0x72,0x39,0x39,0x4b}, +{0x94,0x4a,0x4a,0xde}, {0x98,0x4c,0x4c,0xd4}, {0xb0,0x58,0x58,0xe8}, {0x85,0xcf,0xcf,0x4a}, +{0xbb,0xd0,0xd0,0x6b}, {0xc5,0xef,0xef,0x2a}, {0x4f,0xaa,0xaa,0xe5}, {0xed,0xfb,0xfb,0x16}, +{0x86,0x43,0x43,0xc5}, {0x9a,0x4d,0x4d,0xd7}, {0x66,0x33,0x33,0x55}, {0x11,0x85,0x85,0x94}, +{0x8a,0x45,0x45,0xcf}, {0xe9,0xf9,0xf9,0x10}, {0x04,0x02,0x02,0x06}, {0xfe,0x7f,0x7f,0x81}, +{0xa0,0x50,0x50,0xf0}, {0x78,0x3c,0x3c,0x44}, {0x25,0x9f,0x9f,0xba}, {0x4b,0xa8,0xa8,0xe3}, +{0xa2,0x51,0x51,0xf3}, {0x5d,0xa3,0xa3,0xfe}, {0x80,0x40,0x40,0xc0}, {0x05,0x8f,0x8f,0x8a}, +{0x3f,0x92,0x92,0xad}, {0x21,0x9d,0x9d,0xbc}, {0x70,0x38,0x38,0x48}, {0xf1,0xf5,0xf5,0x04}, +{0x63,0xbc,0xbc,0xdf}, {0x77,0xb6,0xb6,0xc1}, {0xaf,0xda,0xda,0x75}, {0x42,0x21,0x21,0x63}, +{0x20,0x10,0x10,0x30}, {0xe5,0xff,0xff,0x1a}, {0xfd,0xf3,0xf3,0x0e}, {0xbf,0xd2,0xd2,0x6d}, +{0x81,0xcd,0xcd,0x4c}, {0x18,0x0c,0x0c,0x14}, {0x26,0x13,0x13,0x35}, {0xc3,0xec,0xec,0x2f}, +{0xbe,0x5f,0x5f,0xe1}, {0x35,0x97,0x97,0xa2}, {0x88,0x44,0x44,0xcc}, {0x2e,0x17,0x17,0x39}, +{0x93,0xc4,0xc4,0x57}, {0x55,0xa7,0xa7,0xf2}, {0xfc,0x7e,0x7e,0x82}, {0x7a,0x3d,0x3d,0x47}, +{0xc8,0x64,0x64,0xac}, {0xba,0x5d,0x5d,0xe7}, {0x32,0x19,0x19,0x2b}, {0xe6,0x73,0x73,0x95}, +{0xc0,0x60,0x60,0xa0}, {0x19,0x81,0x81,0x98}, {0x9e,0x4f,0x4f,0xd1}, {0xa3,0xdc,0xdc,0x7f}, +{0x44,0x22,0x22,0x66}, {0x54,0x2a,0x2a,0x7e}, {0x3b,0x90,0x90,0xab}, {0x0b,0x88,0x88,0x83}, +{0x8c,0x46,0x46,0xca}, {0xc7,0xee,0xee,0x29}, {0x6b,0xb8,0xb8,0xd3}, {0x28,0x14,0x14,0x3c}, +{0xa7,0xde,0xde,0x79}, {0xbc,0x5e,0x5e,0xe2}, {0x16,0x0b,0x0b,0x1d}, {0xad,0xdb,0xdb,0x76}, +{0xdb,0xe0,0xe0,0x3b}, {0x64,0x32,0x32,0x56}, {0x74,0x3a,0x3a,0x4e}, {0x14,0x0a,0x0a,0x1e}, +{0x92,0x49,0x49,0xdb}, {0x0c,0x06,0x06,0x0a}, {0x48,0x24,0x24,0x6c}, {0xb8,0x5c,0x5c,0xe4}, +{0x9f,0xc2,0xc2,0x5d}, {0xbd,0xd3,0xd3,0x6e}, {0x43,0xac,0xac,0xef}, {0xc4,0x62,0x62,0xa6}, +{0x39,0x91,0x91,0xa8}, {0x31,0x95,0x95,0xa4}, {0xd3,0xe4,0xe4,0x37}, {0xf2,0x79,0x79,0x8b}, +{0xd5,0xe7,0xe7,0x32}, {0x8b,0xc8,0xc8,0x43}, {0x6e,0x37,0x37,0x59}, {0xda,0x6d,0x6d,0xb7}, +{0x01,0x8d,0x8d,0x8c}, {0xb1,0xd5,0xd5,0x64}, {0x9c,0x4e,0x4e,0xd2}, {0x49,0xa9,0xa9,0xe0}, +{0xd8,0x6c,0x6c,0xb4}, {0xac,0x56,0x56,0xfa}, {0xf3,0xf4,0xf4,0x07}, {0xcf,0xea,0xea,0x25}, +{0xca,0x65,0x65,0xaf}, {0xf4,0x7a,0x7a,0x8e}, {0x47,0xae,0xae,0xe9}, {0x10,0x08,0x08,0x18}, +{0x6f,0xba,0xba,0xd5}, {0xf0,0x78,0x78,0x88}, {0x4a,0x25,0x25,0x6f}, {0x5c,0x2e,0x2e,0x72}, +{0x38,0x1c,0x1c,0x24}, {0x57,0xa6,0xa6,0xf1}, {0x73,0xb4,0xb4,0xc7}, {0x97,0xc6,0xc6,0x51}, +{0xcb,0xe8,0xe8,0x23}, {0xa1,0xdd,0xdd,0x7c}, {0xe8,0x74,0x74,0x9c}, {0x3e,0x1f,0x1f,0x21}, +{0x96,0x4b,0x4b,0xdd}, {0x61,0xbd,0xbd,0xdc}, {0x0d,0x8b,0x8b,0x86}, {0x0f,0x8a,0x8a,0x85}, +{0xe0,0x70,0x70,0x90}, {0x7c,0x3e,0x3e,0x42}, {0x71,0xb5,0xb5,0xc4}, {0xcc,0x66,0x66,0xaa}, +{0x90,0x48,0x48,0xd8}, {0x06,0x03,0x03,0x05}, {0xf7,0xf6,0xf6,0x01}, {0x1c,0x0e,0x0e,0x12}, +{0xc2,0x61,0x61,0xa3}, {0x6a,0x35,0x35,0x5f}, {0xae,0x57,0x57,0xf9}, {0x69,0xb9,0xb9,0xd0}, +{0x17,0x86,0x86,0x91}, {0x99,0xc1,0xc1,0x58}, {0x3a,0x1d,0x1d,0x27}, {0x27,0x9e,0x9e,0xb9}, +{0xd9,0xe1,0xe1,0x38}, {0xeb,0xf8,0xf8,0x13}, {0x2b,0x98,0x98,0xb3}, {0x22,0x11,0x11,0x33}, +{0xd2,0x69,0x69,0xbb}, {0xa9,0xd9,0xd9,0x70}, {0x07,0x8e,0x8e,0x89}, {0x33,0x94,0x94,0xa7}, +{0x2d,0x9b,0x9b,0xb6}, {0x3c,0x1e,0x1e,0x22}, {0x15,0x87,0x87,0x92}, {0xc9,0xe9,0xe9,0x20}, +{0x87,0xce,0xce,0x49}, {0xaa,0x55,0x55,0xff}, {0x50,0x28,0x28,0x78}, {0xa5,0xdf,0xdf,0x7a}, +{0x03,0x8c,0x8c,0x8f}, {0x59,0xa1,0xa1,0xf8}, {0x09,0x89,0x89,0x80}, {0x1a,0x0d,0x0d,0x17}, +{0x65,0xbf,0xbf,0xda}, {0xd7,0xe6,0xe6,0x31}, {0x84,0x42,0x42,0xc6}, {0xd0,0x68,0x68,0xb8}, +{0x82,0x41,0x41,0xc3}, {0x29,0x99,0x99,0xb0}, {0x5a,0x2d,0x2d,0x77}, {0x1e,0x0f,0x0f,0x11}, +{0x7b,0xb0,0xb0,0xcb}, {0xa8,0x54,0x54,0xfc}, {0x6d,0xbb,0xbb,0xd6}, {0x2c,0x16,0x16,0x3a} +}; + +word8 T2[256][4] = { +{0xa5,0xc6,0x63,0x63}, {0x84,0xf8,0x7c,0x7c}, {0x99,0xee,0x77,0x77}, {0x8d,0xf6,0x7b,0x7b}, +{0x0d,0xff,0xf2,0xf2}, {0xbd,0xd6,0x6b,0x6b}, {0xb1,0xde,0x6f,0x6f}, {0x54,0x91,0xc5,0xc5}, +{0x50,0x60,0x30,0x30}, {0x03,0x02,0x01,0x01}, {0xa9,0xce,0x67,0x67}, {0x7d,0x56,0x2b,0x2b}, +{0x19,0xe7,0xfe,0xfe}, {0x62,0xb5,0xd7,0xd7}, {0xe6,0x4d,0xab,0xab}, {0x9a,0xec,0x76,0x76}, +{0x45,0x8f,0xca,0xca}, {0x9d,0x1f,0x82,0x82}, {0x40,0x89,0xc9,0xc9}, {0x87,0xfa,0x7d,0x7d}, +{0x15,0xef,0xfa,0xfa}, {0xeb,0xb2,0x59,0x59}, {0xc9,0x8e,0x47,0x47}, {0x0b,0xfb,0xf0,0xf0}, +{0xec,0x41,0xad,0xad}, {0x67,0xb3,0xd4,0xd4}, {0xfd,0x5f,0xa2,0xa2}, {0xea,0x45,0xaf,0xaf}, +{0xbf,0x23,0x9c,0x9c}, {0xf7,0x53,0xa4,0xa4}, {0x96,0xe4,0x72,0x72}, {0x5b,0x9b,0xc0,0xc0}, +{0xc2,0x75,0xb7,0xb7}, {0x1c,0xe1,0xfd,0xfd}, {0xae,0x3d,0x93,0x93}, {0x6a,0x4c,0x26,0x26}, +{0x5a,0x6c,0x36,0x36}, {0x41,0x7e,0x3f,0x3f}, {0x02,0xf5,0xf7,0xf7}, {0x4f,0x83,0xcc,0xcc}, +{0x5c,0x68,0x34,0x34}, {0xf4,0x51,0xa5,0xa5}, {0x34,0xd1,0xe5,0xe5}, {0x08,0xf9,0xf1,0xf1}, +{0x93,0xe2,0x71,0x71}, {0x73,0xab,0xd8,0xd8}, {0x53,0x62,0x31,0x31}, {0x3f,0x2a,0x15,0x15}, +{0x0c,0x08,0x04,0x04}, {0x52,0x95,0xc7,0xc7}, {0x65,0x46,0x23,0x23}, {0x5e,0x9d,0xc3,0xc3}, +{0x28,0x30,0x18,0x18}, {0xa1,0x37,0x96,0x96}, {0x0f,0x0a,0x05,0x05}, {0xb5,0x2f,0x9a,0x9a}, +{0x09,0x0e,0x07,0x07}, {0x36,0x24,0x12,0x12}, {0x9b,0x1b,0x80,0x80}, {0x3d,0xdf,0xe2,0xe2}, +{0x26,0xcd,0xeb,0xeb}, {0x69,0x4e,0x27,0x27}, {0xcd,0x7f,0xb2,0xb2}, {0x9f,0xea,0x75,0x75}, +{0x1b,0x12,0x09,0x09}, {0x9e,0x1d,0x83,0x83}, {0x74,0x58,0x2c,0x2c}, {0x2e,0x34,0x1a,0x1a}, +{0x2d,0x36,0x1b,0x1b}, {0xb2,0xdc,0x6e,0x6e}, {0xee,0xb4,0x5a,0x5a}, {0xfb,0x5b,0xa0,0xa0}, +{0xf6,0xa4,0x52,0x52}, {0x4d,0x76,0x3b,0x3b}, {0x61,0xb7,0xd6,0xd6}, {0xce,0x7d,0xb3,0xb3}, +{0x7b,0x52,0x29,0x29}, {0x3e,0xdd,0xe3,0xe3}, {0x71,0x5e,0x2f,0x2f}, {0x97,0x13,0x84,0x84}, +{0xf5,0xa6,0x53,0x53}, {0x68,0xb9,0xd1,0xd1}, {0x00,0x00,0x00,0x00}, {0x2c,0xc1,0xed,0xed}, +{0x60,0x40,0x20,0x20}, {0x1f,0xe3,0xfc,0xfc}, {0xc8,0x79,0xb1,0xb1}, {0xed,0xb6,0x5b,0x5b}, +{0xbe,0xd4,0x6a,0x6a}, {0x46,0x8d,0xcb,0xcb}, {0xd9,0x67,0xbe,0xbe}, {0x4b,0x72,0x39,0x39}, +{0xde,0x94,0x4a,0x4a}, {0xd4,0x98,0x4c,0x4c}, {0xe8,0xb0,0x58,0x58}, {0x4a,0x85,0xcf,0xcf}, +{0x6b,0xbb,0xd0,0xd0}, {0x2a,0xc5,0xef,0xef}, {0xe5,0x4f,0xaa,0xaa}, {0x16,0xed,0xfb,0xfb}, +{0xc5,0x86,0x43,0x43}, {0xd7,0x9a,0x4d,0x4d}, {0x55,0x66,0x33,0x33}, {0x94,0x11,0x85,0x85}, +{0xcf,0x8a,0x45,0x45}, {0x10,0xe9,0xf9,0xf9}, {0x06,0x04,0x02,0x02}, {0x81,0xfe,0x7f,0x7f}, +{0xf0,0xa0,0x50,0x50}, {0x44,0x78,0x3c,0x3c}, {0xba,0x25,0x9f,0x9f}, {0xe3,0x4b,0xa8,0xa8}, +{0xf3,0xa2,0x51,0x51}, {0xfe,0x5d,0xa3,0xa3}, {0xc0,0x80,0x40,0x40}, {0x8a,0x05,0x8f,0x8f}, +{0xad,0x3f,0x92,0x92}, {0xbc,0x21,0x9d,0x9d}, {0x48,0x70,0x38,0x38}, {0x04,0xf1,0xf5,0xf5}, +{0xdf,0x63,0xbc,0xbc}, {0xc1,0x77,0xb6,0xb6}, {0x75,0xaf,0xda,0xda}, {0x63,0x42,0x21,0x21}, +{0x30,0x20,0x10,0x10}, {0x1a,0xe5,0xff,0xff}, {0x0e,0xfd,0xf3,0xf3}, {0x6d,0xbf,0xd2,0xd2}, +{0x4c,0x81,0xcd,0xcd}, {0x14,0x18,0x0c,0x0c}, {0x35,0x26,0x13,0x13}, {0x2f,0xc3,0xec,0xec}, +{0xe1,0xbe,0x5f,0x5f}, {0xa2,0x35,0x97,0x97}, {0xcc,0x88,0x44,0x44}, {0x39,0x2e,0x17,0x17}, +{0x57,0x93,0xc4,0xc4}, {0xf2,0x55,0xa7,0xa7}, {0x82,0xfc,0x7e,0x7e}, {0x47,0x7a,0x3d,0x3d}, +{0xac,0xc8,0x64,0x64}, {0xe7,0xba,0x5d,0x5d}, {0x2b,0x32,0x19,0x19}, {0x95,0xe6,0x73,0x73}, +{0xa0,0xc0,0x60,0x60}, {0x98,0x19,0x81,0x81}, {0xd1,0x9e,0x4f,0x4f}, {0x7f,0xa3,0xdc,0xdc}, +{0x66,0x44,0x22,0x22}, {0x7e,0x54,0x2a,0x2a}, {0xab,0x3b,0x90,0x90}, {0x83,0x0b,0x88,0x88}, +{0xca,0x8c,0x46,0x46}, {0x29,0xc7,0xee,0xee}, {0xd3,0x6b,0xb8,0xb8}, {0x3c,0x28,0x14,0x14}, +{0x79,0xa7,0xde,0xde}, {0xe2,0xbc,0x5e,0x5e}, {0x1d,0x16,0x0b,0x0b}, {0x76,0xad,0xdb,0xdb}, +{0x3b,0xdb,0xe0,0xe0}, {0x56,0x64,0x32,0x32}, {0x4e,0x74,0x3a,0x3a}, {0x1e,0x14,0x0a,0x0a}, +{0xdb,0x92,0x49,0x49}, {0x0a,0x0c,0x06,0x06}, {0x6c,0x48,0x24,0x24}, {0xe4,0xb8,0x5c,0x5c}, +{0x5d,0x9f,0xc2,0xc2}, {0x6e,0xbd,0xd3,0xd3}, {0xef,0x43,0xac,0xac}, {0xa6,0xc4,0x62,0x62}, +{0xa8,0x39,0x91,0x91}, {0xa4,0x31,0x95,0x95}, {0x37,0xd3,0xe4,0xe4}, {0x8b,0xf2,0x79,0x79}, +{0x32,0xd5,0xe7,0xe7}, {0x43,0x8b,0xc8,0xc8}, {0x59,0x6e,0x37,0x37}, {0xb7,0xda,0x6d,0x6d}, +{0x8c,0x01,0x8d,0x8d}, {0x64,0xb1,0xd5,0xd5}, {0xd2,0x9c,0x4e,0x4e}, {0xe0,0x49,0xa9,0xa9}, +{0xb4,0xd8,0x6c,0x6c}, {0xfa,0xac,0x56,0x56}, {0x07,0xf3,0xf4,0xf4}, {0x25,0xcf,0xea,0xea}, +{0xaf,0xca,0x65,0x65}, {0x8e,0xf4,0x7a,0x7a}, {0xe9,0x47,0xae,0xae}, {0x18,0x10,0x08,0x08}, +{0xd5,0x6f,0xba,0xba}, {0x88,0xf0,0x78,0x78}, {0x6f,0x4a,0x25,0x25}, {0x72,0x5c,0x2e,0x2e}, +{0x24,0x38,0x1c,0x1c}, {0xf1,0x57,0xa6,0xa6}, {0xc7,0x73,0xb4,0xb4}, {0x51,0x97,0xc6,0xc6}, +{0x23,0xcb,0xe8,0xe8}, {0x7c,0xa1,0xdd,0xdd}, {0x9c,0xe8,0x74,0x74}, {0x21,0x3e,0x1f,0x1f}, +{0xdd,0x96,0x4b,0x4b}, {0xdc,0x61,0xbd,0xbd}, {0x86,0x0d,0x8b,0x8b}, {0x85,0x0f,0x8a,0x8a}, +{0x90,0xe0,0x70,0x70}, {0x42,0x7c,0x3e,0x3e}, {0xc4,0x71,0xb5,0xb5}, {0xaa,0xcc,0x66,0x66}, +{0xd8,0x90,0x48,0x48}, {0x05,0x06,0x03,0x03}, {0x01,0xf7,0xf6,0xf6}, {0x12,0x1c,0x0e,0x0e}, +{0xa3,0xc2,0x61,0x61}, {0x5f,0x6a,0x35,0x35}, {0xf9,0xae,0x57,0x57}, {0xd0,0x69,0xb9,0xb9}, +{0x91,0x17,0x86,0x86}, {0x58,0x99,0xc1,0xc1}, {0x27,0x3a,0x1d,0x1d}, {0xb9,0x27,0x9e,0x9e}, +{0x38,0xd9,0xe1,0xe1}, {0x13,0xeb,0xf8,0xf8}, {0xb3,0x2b,0x98,0x98}, {0x33,0x22,0x11,0x11}, +{0xbb,0xd2,0x69,0x69}, {0x70,0xa9,0xd9,0xd9}, {0x89,0x07,0x8e,0x8e}, {0xa7,0x33,0x94,0x94}, +{0xb6,0x2d,0x9b,0x9b}, {0x22,0x3c,0x1e,0x1e}, {0x92,0x15,0x87,0x87}, {0x20,0xc9,0xe9,0xe9}, +{0x49,0x87,0xce,0xce}, {0xff,0xaa,0x55,0x55}, {0x78,0x50,0x28,0x28}, {0x7a,0xa5,0xdf,0xdf}, +{0x8f,0x03,0x8c,0x8c}, {0xf8,0x59,0xa1,0xa1}, {0x80,0x09,0x89,0x89}, {0x17,0x1a,0x0d,0x0d}, +{0xda,0x65,0xbf,0xbf}, {0x31,0xd7,0xe6,0xe6}, {0xc6,0x84,0x42,0x42}, {0xb8,0xd0,0x68,0x68}, +{0xc3,0x82,0x41,0x41}, {0xb0,0x29,0x99,0x99}, {0x77,0x5a,0x2d,0x2d}, {0x11,0x1e,0x0f,0x0f}, +{0xcb,0x7b,0xb0,0xb0}, {0xfc,0xa8,0x54,0x54}, {0xd6,0x6d,0xbb,0xbb}, {0x3a,0x2c,0x16,0x16} +}; + +word8 T3[256][4] = { +{0x63,0xa5,0xc6,0x63}, {0x7c,0x84,0xf8,0x7c}, {0x77,0x99,0xee,0x77}, {0x7b,0x8d,0xf6,0x7b}, +{0xf2,0x0d,0xff,0xf2}, {0x6b,0xbd,0xd6,0x6b}, {0x6f,0xb1,0xde,0x6f}, {0xc5,0x54,0x91,0xc5}, +{0x30,0x50,0x60,0x30}, {0x01,0x03,0x02,0x01}, {0x67,0xa9,0xce,0x67}, {0x2b,0x7d,0x56,0x2b}, +{0xfe,0x19,0xe7,0xfe}, {0xd7,0x62,0xb5,0xd7}, {0xab,0xe6,0x4d,0xab}, {0x76,0x9a,0xec,0x76}, +{0xca,0x45,0x8f,0xca}, {0x82,0x9d,0x1f,0x82}, {0xc9,0x40,0x89,0xc9}, {0x7d,0x87,0xfa,0x7d}, +{0xfa,0x15,0xef,0xfa}, {0x59,0xeb,0xb2,0x59}, {0x47,0xc9,0x8e,0x47}, {0xf0,0x0b,0xfb,0xf0}, +{0xad,0xec,0x41,0xad}, {0xd4,0x67,0xb3,0xd4}, {0xa2,0xfd,0x5f,0xa2}, {0xaf,0xea,0x45,0xaf}, +{0x9c,0xbf,0x23,0x9c}, {0xa4,0xf7,0x53,0xa4}, {0x72,0x96,0xe4,0x72}, {0xc0,0x5b,0x9b,0xc0}, +{0xb7,0xc2,0x75,0xb7}, {0xfd,0x1c,0xe1,0xfd}, {0x93,0xae,0x3d,0x93}, {0x26,0x6a,0x4c,0x26}, +{0x36,0x5a,0x6c,0x36}, {0x3f,0x41,0x7e,0x3f}, {0xf7,0x02,0xf5,0xf7}, {0xcc,0x4f,0x83,0xcc}, +{0x34,0x5c,0x68,0x34}, {0xa5,0xf4,0x51,0xa5}, {0xe5,0x34,0xd1,0xe5}, {0xf1,0x08,0xf9,0xf1}, +{0x71,0x93,0xe2,0x71}, {0xd8,0x73,0xab,0xd8}, {0x31,0x53,0x62,0x31}, {0x15,0x3f,0x2a,0x15}, +{0x04,0x0c,0x08,0x04}, {0xc7,0x52,0x95,0xc7}, {0x23,0x65,0x46,0x23}, {0xc3,0x5e,0x9d,0xc3}, +{0x18,0x28,0x30,0x18}, {0x96,0xa1,0x37,0x96}, {0x05,0x0f,0x0a,0x05}, {0x9a,0xb5,0x2f,0x9a}, +{0x07,0x09,0x0e,0x07}, {0x12,0x36,0x24,0x12}, {0x80,0x9b,0x1b,0x80}, {0xe2,0x3d,0xdf,0xe2}, +{0xeb,0x26,0xcd,0xeb}, {0x27,0x69,0x4e,0x27}, {0xb2,0xcd,0x7f,0xb2}, {0x75,0x9f,0xea,0x75}, +{0x09,0x1b,0x12,0x09}, {0x83,0x9e,0x1d,0x83}, {0x2c,0x74,0x58,0x2c}, {0x1a,0x2e,0x34,0x1a}, +{0x1b,0x2d,0x36,0x1b}, {0x6e,0xb2,0xdc,0x6e}, {0x5a,0xee,0xb4,0x5a}, {0xa0,0xfb,0x5b,0xa0}, +{0x52,0xf6,0xa4,0x52}, {0x3b,0x4d,0x76,0x3b}, {0xd6,0x61,0xb7,0xd6}, {0xb3,0xce,0x7d,0xb3}, +{0x29,0x7b,0x52,0x29}, {0xe3,0x3e,0xdd,0xe3}, {0x2f,0x71,0x5e,0x2f}, {0x84,0x97,0x13,0x84}, +{0x53,0xf5,0xa6,0x53}, {0xd1,0x68,0xb9,0xd1}, {0x00,0x00,0x00,0x00}, {0xed,0x2c,0xc1,0xed}, +{0x20,0x60,0x40,0x20}, {0xfc,0x1f,0xe3,0xfc}, {0xb1,0xc8,0x79,0xb1}, {0x5b,0xed,0xb6,0x5b}, +{0x6a,0xbe,0xd4,0x6a}, {0xcb,0x46,0x8d,0xcb}, {0xbe,0xd9,0x67,0xbe}, {0x39,0x4b,0x72,0x39}, +{0x4a,0xde,0x94,0x4a}, {0x4c,0xd4,0x98,0x4c}, {0x58,0xe8,0xb0,0x58}, {0xcf,0x4a,0x85,0xcf}, +{0xd0,0x6b,0xbb,0xd0}, {0xef,0x2a,0xc5,0xef}, {0xaa,0xe5,0x4f,0xaa}, {0xfb,0x16,0xed,0xfb}, +{0x43,0xc5,0x86,0x43}, {0x4d,0xd7,0x9a,0x4d}, {0x33,0x55,0x66,0x33}, {0x85,0x94,0x11,0x85}, +{0x45,0xcf,0x8a,0x45}, {0xf9,0x10,0xe9,0xf9}, {0x02,0x06,0x04,0x02}, {0x7f,0x81,0xfe,0x7f}, +{0x50,0xf0,0xa0,0x50}, {0x3c,0x44,0x78,0x3c}, {0x9f,0xba,0x25,0x9f}, {0xa8,0xe3,0x4b,0xa8}, +{0x51,0xf3,0xa2,0x51}, {0xa3,0xfe,0x5d,0xa3}, {0x40,0xc0,0x80,0x40}, {0x8f,0x8a,0x05,0x8f}, +{0x92,0xad,0x3f,0x92}, {0x9d,0xbc,0x21,0x9d}, {0x38,0x48,0x70,0x38}, {0xf5,0x04,0xf1,0xf5}, +{0xbc,0xdf,0x63,0xbc}, {0xb6,0xc1,0x77,0xb6}, {0xda,0x75,0xaf,0xda}, {0x21,0x63,0x42,0x21}, +{0x10,0x30,0x20,0x10}, {0xff,0x1a,0xe5,0xff}, {0xf3,0x0e,0xfd,0xf3}, {0xd2,0x6d,0xbf,0xd2}, +{0xcd,0x4c,0x81,0xcd}, {0x0c,0x14,0x18,0x0c}, {0x13,0x35,0x26,0x13}, {0xec,0x2f,0xc3,0xec}, +{0x5f,0xe1,0xbe,0x5f}, {0x97,0xa2,0x35,0x97}, {0x44,0xcc,0x88,0x44}, {0x17,0x39,0x2e,0x17}, +{0xc4,0x57,0x93,0xc4}, {0xa7,0xf2,0x55,0xa7}, {0x7e,0x82,0xfc,0x7e}, {0x3d,0x47,0x7a,0x3d}, +{0x64,0xac,0xc8,0x64}, {0x5d,0xe7,0xba,0x5d}, {0x19,0x2b,0x32,0x19}, {0x73,0x95,0xe6,0x73}, +{0x60,0xa0,0xc0,0x60}, {0x81,0x98,0x19,0x81}, {0x4f,0xd1,0x9e,0x4f}, {0xdc,0x7f,0xa3,0xdc}, +{0x22,0x66,0x44,0x22}, {0x2a,0x7e,0x54,0x2a}, {0x90,0xab,0x3b,0x90}, {0x88,0x83,0x0b,0x88}, +{0x46,0xca,0x8c,0x46}, {0xee,0x29,0xc7,0xee}, {0xb8,0xd3,0x6b,0xb8}, {0x14,0x3c,0x28,0x14}, +{0xde,0x79,0xa7,0xde}, {0x5e,0xe2,0xbc,0x5e}, {0x0b,0x1d,0x16,0x0b}, {0xdb,0x76,0xad,0xdb}, +{0xe0,0x3b,0xdb,0xe0}, {0x32,0x56,0x64,0x32}, {0x3a,0x4e,0x74,0x3a}, {0x0a,0x1e,0x14,0x0a}, +{0x49,0xdb,0x92,0x49}, {0x06,0x0a,0x0c,0x06}, {0x24,0x6c,0x48,0x24}, {0x5c,0xe4,0xb8,0x5c}, +{0xc2,0x5d,0x9f,0xc2}, {0xd3,0x6e,0xbd,0xd3}, {0xac,0xef,0x43,0xac}, {0x62,0xa6,0xc4,0x62}, +{0x91,0xa8,0x39,0x91}, {0x95,0xa4,0x31,0x95}, {0xe4,0x37,0xd3,0xe4}, {0x79,0x8b,0xf2,0x79}, +{0xe7,0x32,0xd5,0xe7}, {0xc8,0x43,0x8b,0xc8}, {0x37,0x59,0x6e,0x37}, {0x6d,0xb7,0xda,0x6d}, +{0x8d,0x8c,0x01,0x8d}, {0xd5,0x64,0xb1,0xd5}, {0x4e,0xd2,0x9c,0x4e}, {0xa9,0xe0,0x49,0xa9}, +{0x6c,0xb4,0xd8,0x6c}, {0x56,0xfa,0xac,0x56}, {0xf4,0x07,0xf3,0xf4}, {0xea,0x25,0xcf,0xea}, +{0x65,0xaf,0xca,0x65}, {0x7a,0x8e,0xf4,0x7a}, {0xae,0xe9,0x47,0xae}, {0x08,0x18,0x10,0x08}, +{0xba,0xd5,0x6f,0xba}, {0x78,0x88,0xf0,0x78}, {0x25,0x6f,0x4a,0x25}, {0x2e,0x72,0x5c,0x2e}, +{0x1c,0x24,0x38,0x1c}, {0xa6,0xf1,0x57,0xa6}, {0xb4,0xc7,0x73,0xb4}, {0xc6,0x51,0x97,0xc6}, +{0xe8,0x23,0xcb,0xe8}, {0xdd,0x7c,0xa1,0xdd}, {0x74,0x9c,0xe8,0x74}, {0x1f,0x21,0x3e,0x1f}, +{0x4b,0xdd,0x96,0x4b}, {0xbd,0xdc,0x61,0xbd}, {0x8b,0x86,0x0d,0x8b}, {0x8a,0x85,0x0f,0x8a}, +{0x70,0x90,0xe0,0x70}, {0x3e,0x42,0x7c,0x3e}, {0xb5,0xc4,0x71,0xb5}, {0x66,0xaa,0xcc,0x66}, +{0x48,0xd8,0x90,0x48}, {0x03,0x05,0x06,0x03}, {0xf6,0x01,0xf7,0xf6}, {0x0e,0x12,0x1c,0x0e}, +{0x61,0xa3,0xc2,0x61}, {0x35,0x5f,0x6a,0x35}, {0x57,0xf9,0xae,0x57}, {0xb9,0xd0,0x69,0xb9}, +{0x86,0x91,0x17,0x86}, {0xc1,0x58,0x99,0xc1}, {0x1d,0x27,0x3a,0x1d}, {0x9e,0xb9,0x27,0x9e}, +{0xe1,0x38,0xd9,0xe1}, {0xf8,0x13,0xeb,0xf8}, {0x98,0xb3,0x2b,0x98}, {0x11,0x33,0x22,0x11}, +{0x69,0xbb,0xd2,0x69}, {0xd9,0x70,0xa9,0xd9}, {0x8e,0x89,0x07,0x8e}, {0x94,0xa7,0x33,0x94}, +{0x9b,0xb6,0x2d,0x9b}, {0x1e,0x22,0x3c,0x1e}, {0x87,0x92,0x15,0x87}, {0xe9,0x20,0xc9,0xe9}, +{0xce,0x49,0x87,0xce}, {0x55,0xff,0xaa,0x55}, {0x28,0x78,0x50,0x28}, {0xdf,0x7a,0xa5,0xdf}, +{0x8c,0x8f,0x03,0x8c}, {0xa1,0xf8,0x59,0xa1}, {0x89,0x80,0x09,0x89}, {0x0d,0x17,0x1a,0x0d}, +{0xbf,0xda,0x65,0xbf}, {0xe6,0x31,0xd7,0xe6}, {0x42,0xc6,0x84,0x42}, {0x68,0xb8,0xd0,0x68}, +{0x41,0xc3,0x82,0x41}, {0x99,0xb0,0x29,0x99}, {0x2d,0x77,0x5a,0x2d}, {0x0f,0x11,0x1e,0x0f}, +{0xb0,0xcb,0x7b,0xb0}, {0x54,0xfc,0xa8,0x54}, {0xbb,0xd6,0x6d,0xbb}, {0x16,0x3a,0x2c,0x16} +}; + +word8 T4[256][4] = { +{0x63,0x63,0xa5,0xc6}, {0x7c,0x7c,0x84,0xf8}, {0x77,0x77,0x99,0xee}, {0x7b,0x7b,0x8d,0xf6}, +{0xf2,0xf2,0x0d,0xff}, {0x6b,0x6b,0xbd,0xd6}, {0x6f,0x6f,0xb1,0xde}, {0xc5,0xc5,0x54,0x91}, +{0x30,0x30,0x50,0x60}, {0x01,0x01,0x03,0x02}, {0x67,0x67,0xa9,0xce}, {0x2b,0x2b,0x7d,0x56}, +{0xfe,0xfe,0x19,0xe7}, {0xd7,0xd7,0x62,0xb5}, {0xab,0xab,0xe6,0x4d}, {0x76,0x76,0x9a,0xec}, +{0xca,0xca,0x45,0x8f}, {0x82,0x82,0x9d,0x1f}, {0xc9,0xc9,0x40,0x89}, {0x7d,0x7d,0x87,0xfa}, +{0xfa,0xfa,0x15,0xef}, {0x59,0x59,0xeb,0xb2}, {0x47,0x47,0xc9,0x8e}, {0xf0,0xf0,0x0b,0xfb}, +{0xad,0xad,0xec,0x41}, {0xd4,0xd4,0x67,0xb3}, {0xa2,0xa2,0xfd,0x5f}, {0xaf,0xaf,0xea,0x45}, +{0x9c,0x9c,0xbf,0x23}, {0xa4,0xa4,0xf7,0x53}, {0x72,0x72,0x96,0xe4}, {0xc0,0xc0,0x5b,0x9b}, +{0xb7,0xb7,0xc2,0x75}, {0xfd,0xfd,0x1c,0xe1}, {0x93,0x93,0xae,0x3d}, {0x26,0x26,0x6a,0x4c}, +{0x36,0x36,0x5a,0x6c}, {0x3f,0x3f,0x41,0x7e}, {0xf7,0xf7,0x02,0xf5}, {0xcc,0xcc,0x4f,0x83}, +{0x34,0x34,0x5c,0x68}, {0xa5,0xa5,0xf4,0x51}, {0xe5,0xe5,0x34,0xd1}, {0xf1,0xf1,0x08,0xf9}, +{0x71,0x71,0x93,0xe2}, {0xd8,0xd8,0x73,0xab}, {0x31,0x31,0x53,0x62}, {0x15,0x15,0x3f,0x2a}, +{0x04,0x04,0x0c,0x08}, {0xc7,0xc7,0x52,0x95}, {0x23,0x23,0x65,0x46}, {0xc3,0xc3,0x5e,0x9d}, +{0x18,0x18,0x28,0x30}, {0x96,0x96,0xa1,0x37}, {0x05,0x05,0x0f,0x0a}, {0x9a,0x9a,0xb5,0x2f}, +{0x07,0x07,0x09,0x0e}, {0x12,0x12,0x36,0x24}, {0x80,0x80,0x9b,0x1b}, {0xe2,0xe2,0x3d,0xdf}, +{0xeb,0xeb,0x26,0xcd}, {0x27,0x27,0x69,0x4e}, {0xb2,0xb2,0xcd,0x7f}, {0x75,0x75,0x9f,0xea}, +{0x09,0x09,0x1b,0x12}, {0x83,0x83,0x9e,0x1d}, {0x2c,0x2c,0x74,0x58}, {0x1a,0x1a,0x2e,0x34}, +{0x1b,0x1b,0x2d,0x36}, {0x6e,0x6e,0xb2,0xdc}, {0x5a,0x5a,0xee,0xb4}, {0xa0,0xa0,0xfb,0x5b}, +{0x52,0x52,0xf6,0xa4}, {0x3b,0x3b,0x4d,0x76}, {0xd6,0xd6,0x61,0xb7}, {0xb3,0xb3,0xce,0x7d}, +{0x29,0x29,0x7b,0x52}, {0xe3,0xe3,0x3e,0xdd}, {0x2f,0x2f,0x71,0x5e}, {0x84,0x84,0x97,0x13}, +{0x53,0x53,0xf5,0xa6}, {0xd1,0xd1,0x68,0xb9}, {0x00,0x00,0x00,0x00}, {0xed,0xed,0x2c,0xc1}, +{0x20,0x20,0x60,0x40}, {0xfc,0xfc,0x1f,0xe3}, {0xb1,0xb1,0xc8,0x79}, {0x5b,0x5b,0xed,0xb6}, +{0x6a,0x6a,0xbe,0xd4}, {0xcb,0xcb,0x46,0x8d}, {0xbe,0xbe,0xd9,0x67}, {0x39,0x39,0x4b,0x72}, +{0x4a,0x4a,0xde,0x94}, {0x4c,0x4c,0xd4,0x98}, {0x58,0x58,0xe8,0xb0}, {0xcf,0xcf,0x4a,0x85}, +{0xd0,0xd0,0x6b,0xbb}, {0xef,0xef,0x2a,0xc5}, {0xaa,0xaa,0xe5,0x4f}, {0xfb,0xfb,0x16,0xed}, +{0x43,0x43,0xc5,0x86}, {0x4d,0x4d,0xd7,0x9a}, {0x33,0x33,0x55,0x66}, {0x85,0x85,0x94,0x11}, +{0x45,0x45,0xcf,0x8a}, {0xf9,0xf9,0x10,0xe9}, {0x02,0x02,0x06,0x04}, {0x7f,0x7f,0x81,0xfe}, +{0x50,0x50,0xf0,0xa0}, {0x3c,0x3c,0x44,0x78}, {0x9f,0x9f,0xba,0x25}, {0xa8,0xa8,0xe3,0x4b}, +{0x51,0x51,0xf3,0xa2}, {0xa3,0xa3,0xfe,0x5d}, {0x40,0x40,0xc0,0x80}, {0x8f,0x8f,0x8a,0x05}, +{0x92,0x92,0xad,0x3f}, {0x9d,0x9d,0xbc,0x21}, {0x38,0x38,0x48,0x70}, {0xf5,0xf5,0x04,0xf1}, +{0xbc,0xbc,0xdf,0x63}, {0xb6,0xb6,0xc1,0x77}, {0xda,0xda,0x75,0xaf}, {0x21,0x21,0x63,0x42}, +{0x10,0x10,0x30,0x20}, {0xff,0xff,0x1a,0xe5}, {0xf3,0xf3,0x0e,0xfd}, {0xd2,0xd2,0x6d,0xbf}, +{0xcd,0xcd,0x4c,0x81}, {0x0c,0x0c,0x14,0x18}, {0x13,0x13,0x35,0x26}, {0xec,0xec,0x2f,0xc3}, +{0x5f,0x5f,0xe1,0xbe}, {0x97,0x97,0xa2,0x35}, {0x44,0x44,0xcc,0x88}, {0x17,0x17,0x39,0x2e}, +{0xc4,0xc4,0x57,0x93}, {0xa7,0xa7,0xf2,0x55}, {0x7e,0x7e,0x82,0xfc}, {0x3d,0x3d,0x47,0x7a}, +{0x64,0x64,0xac,0xc8}, {0x5d,0x5d,0xe7,0xba}, {0x19,0x19,0x2b,0x32}, {0x73,0x73,0x95,0xe6}, +{0x60,0x60,0xa0,0xc0}, {0x81,0x81,0x98,0x19}, {0x4f,0x4f,0xd1,0x9e}, {0xdc,0xdc,0x7f,0xa3}, +{0x22,0x22,0x66,0x44}, {0x2a,0x2a,0x7e,0x54}, {0x90,0x90,0xab,0x3b}, {0x88,0x88,0x83,0x0b}, +{0x46,0x46,0xca,0x8c}, {0xee,0xee,0x29,0xc7}, {0xb8,0xb8,0xd3,0x6b}, {0x14,0x14,0x3c,0x28}, +{0xde,0xde,0x79,0xa7}, {0x5e,0x5e,0xe2,0xbc}, {0x0b,0x0b,0x1d,0x16}, {0xdb,0xdb,0x76,0xad}, +{0xe0,0xe0,0x3b,0xdb}, {0x32,0x32,0x56,0x64}, {0x3a,0x3a,0x4e,0x74}, {0x0a,0x0a,0x1e,0x14}, +{0x49,0x49,0xdb,0x92}, {0x06,0x06,0x0a,0x0c}, {0x24,0x24,0x6c,0x48}, {0x5c,0x5c,0xe4,0xb8}, +{0xc2,0xc2,0x5d,0x9f}, {0xd3,0xd3,0x6e,0xbd}, {0xac,0xac,0xef,0x43}, {0x62,0x62,0xa6,0xc4}, +{0x91,0x91,0xa8,0x39}, {0x95,0x95,0xa4,0x31}, {0xe4,0xe4,0x37,0xd3}, {0x79,0x79,0x8b,0xf2}, +{0xe7,0xe7,0x32,0xd5}, {0xc8,0xc8,0x43,0x8b}, {0x37,0x37,0x59,0x6e}, {0x6d,0x6d,0xb7,0xda}, +{0x8d,0x8d,0x8c,0x01}, {0xd5,0xd5,0x64,0xb1}, {0x4e,0x4e,0xd2,0x9c}, {0xa9,0xa9,0xe0,0x49}, +{0x6c,0x6c,0xb4,0xd8}, {0x56,0x56,0xfa,0xac}, {0xf4,0xf4,0x07,0xf3}, {0xea,0xea,0x25,0xcf}, +{0x65,0x65,0xaf,0xca}, {0x7a,0x7a,0x8e,0xf4}, {0xae,0xae,0xe9,0x47}, {0x08,0x08,0x18,0x10}, +{0xba,0xba,0xd5,0x6f}, {0x78,0x78,0x88,0xf0}, {0x25,0x25,0x6f,0x4a}, {0x2e,0x2e,0x72,0x5c}, +{0x1c,0x1c,0x24,0x38}, {0xa6,0xa6,0xf1,0x57}, {0xb4,0xb4,0xc7,0x73}, {0xc6,0xc6,0x51,0x97}, +{0xe8,0xe8,0x23,0xcb}, {0xdd,0xdd,0x7c,0xa1}, {0x74,0x74,0x9c,0xe8}, {0x1f,0x1f,0x21,0x3e}, +{0x4b,0x4b,0xdd,0x96}, {0xbd,0xbd,0xdc,0x61}, {0x8b,0x8b,0x86,0x0d}, {0x8a,0x8a,0x85,0x0f}, +{0x70,0x70,0x90,0xe0}, {0x3e,0x3e,0x42,0x7c}, {0xb5,0xb5,0xc4,0x71}, {0x66,0x66,0xaa,0xcc}, +{0x48,0x48,0xd8,0x90}, {0x03,0x03,0x05,0x06}, {0xf6,0xf6,0x01,0xf7}, {0x0e,0x0e,0x12,0x1c}, +{0x61,0x61,0xa3,0xc2}, {0x35,0x35,0x5f,0x6a}, {0x57,0x57,0xf9,0xae}, {0xb9,0xb9,0xd0,0x69}, +{0x86,0x86,0x91,0x17}, {0xc1,0xc1,0x58,0x99}, {0x1d,0x1d,0x27,0x3a}, {0x9e,0x9e,0xb9,0x27}, +{0xe1,0xe1,0x38,0xd9}, {0xf8,0xf8,0x13,0xeb}, {0x98,0x98,0xb3,0x2b}, {0x11,0x11,0x33,0x22}, +{0x69,0x69,0xbb,0xd2}, {0xd9,0xd9,0x70,0xa9}, {0x8e,0x8e,0x89,0x07}, {0x94,0x94,0xa7,0x33}, +{0x9b,0x9b,0xb6,0x2d}, {0x1e,0x1e,0x22,0x3c}, {0x87,0x87,0x92,0x15}, {0xe9,0xe9,0x20,0xc9}, +{0xce,0xce,0x49,0x87}, {0x55,0x55,0xff,0xaa}, {0x28,0x28,0x78,0x50}, {0xdf,0xdf,0x7a,0xa5}, +{0x8c,0x8c,0x8f,0x03}, {0xa1,0xa1,0xf8,0x59}, {0x89,0x89,0x80,0x09}, {0x0d,0x0d,0x17,0x1a}, +{0xbf,0xbf,0xda,0x65}, {0xe6,0xe6,0x31,0xd7}, {0x42,0x42,0xc6,0x84}, {0x68,0x68,0xb8,0xd0}, +{0x41,0x41,0xc3,0x82}, {0x99,0x99,0xb0,0x29}, {0x2d,0x2d,0x77,0x5a}, {0x0f,0x0f,0x11,0x1e}, +{0xb0,0xb0,0xcb,0x7b}, {0x54,0x54,0xfc,0xa8}, {0xbb,0xbb,0xd6,0x6d}, {0x16,0x16,0x3a,0x2c} +}; + +word8 T5[256][4] = { +{0x51,0xf4,0xa7,0x50}, {0x7e,0x41,0x65,0x53}, {0x1a,0x17,0xa4,0xc3}, {0x3a,0x27,0x5e,0x96}, +{0x3b,0xab,0x6b,0xcb}, {0x1f,0x9d,0x45,0xf1}, {0xac,0xfa,0x58,0xab}, {0x4b,0xe3,0x03,0x93}, +{0x20,0x30,0xfa,0x55}, {0xad,0x76,0x6d,0xf6}, {0x88,0xcc,0x76,0x91}, {0xf5,0x02,0x4c,0x25}, +{0x4f,0xe5,0xd7,0xfc}, {0xc5,0x2a,0xcb,0xd7}, {0x26,0x35,0x44,0x80}, {0xb5,0x62,0xa3,0x8f}, +{0xde,0xb1,0x5a,0x49}, {0x25,0xba,0x1b,0x67}, {0x45,0xea,0x0e,0x98}, {0x5d,0xfe,0xc0,0xe1}, +{0xc3,0x2f,0x75,0x02}, {0x81,0x4c,0xf0,0x12}, {0x8d,0x46,0x97,0xa3}, {0x6b,0xd3,0xf9,0xc6}, +{0x03,0x8f,0x5f,0xe7}, {0x15,0x92,0x9c,0x95}, {0xbf,0x6d,0x7a,0xeb}, {0x95,0x52,0x59,0xda}, +{0xd4,0xbe,0x83,0x2d}, {0x58,0x74,0x21,0xd3}, {0x49,0xe0,0x69,0x29}, {0x8e,0xc9,0xc8,0x44}, +{0x75,0xc2,0x89,0x6a}, {0xf4,0x8e,0x79,0x78}, {0x99,0x58,0x3e,0x6b}, {0x27,0xb9,0x71,0xdd}, +{0xbe,0xe1,0x4f,0xb6}, {0xf0,0x88,0xad,0x17}, {0xc9,0x20,0xac,0x66}, {0x7d,0xce,0x3a,0xb4}, +{0x63,0xdf,0x4a,0x18}, {0xe5,0x1a,0x31,0x82}, {0x97,0x51,0x33,0x60}, {0x62,0x53,0x7f,0x45}, +{0xb1,0x64,0x77,0xe0}, {0xbb,0x6b,0xae,0x84}, {0xfe,0x81,0xa0,0x1c}, {0xf9,0x08,0x2b,0x94}, +{0x70,0x48,0x68,0x58}, {0x8f,0x45,0xfd,0x19}, {0x94,0xde,0x6c,0x87}, {0x52,0x7b,0xf8,0xb7}, +{0xab,0x73,0xd3,0x23}, {0x72,0x4b,0x02,0xe2}, {0xe3,0x1f,0x8f,0x57}, {0x66,0x55,0xab,0x2a}, +{0xb2,0xeb,0x28,0x07}, {0x2f,0xb5,0xc2,0x03}, {0x86,0xc5,0x7b,0x9a}, {0xd3,0x37,0x08,0xa5}, +{0x30,0x28,0x87,0xf2}, {0x23,0xbf,0xa5,0xb2}, {0x02,0x03,0x6a,0xba}, {0xed,0x16,0x82,0x5c}, +{0x8a,0xcf,0x1c,0x2b}, {0xa7,0x79,0xb4,0x92}, {0xf3,0x07,0xf2,0xf0}, {0x4e,0x69,0xe2,0xa1}, +{0x65,0xda,0xf4,0xcd}, {0x06,0x05,0xbe,0xd5}, {0xd1,0x34,0x62,0x1f}, {0xc4,0xa6,0xfe,0x8a}, +{0x34,0x2e,0x53,0x9d}, {0xa2,0xf3,0x55,0xa0}, {0x05,0x8a,0xe1,0x32}, {0xa4,0xf6,0xeb,0x75}, +{0x0b,0x83,0xec,0x39}, {0x40,0x60,0xef,0xaa}, {0x5e,0x71,0x9f,0x06}, {0xbd,0x6e,0x10,0x51}, +{0x3e,0x21,0x8a,0xf9}, {0x96,0xdd,0x06,0x3d}, {0xdd,0x3e,0x05,0xae}, {0x4d,0xe6,0xbd,0x46}, +{0x91,0x54,0x8d,0xb5}, {0x71,0xc4,0x5d,0x05}, {0x04,0x06,0xd4,0x6f}, {0x60,0x50,0x15,0xff}, +{0x19,0x98,0xfb,0x24}, {0xd6,0xbd,0xe9,0x97}, {0x89,0x40,0x43,0xcc}, {0x67,0xd9,0x9e,0x77}, +{0xb0,0xe8,0x42,0xbd}, {0x07,0x89,0x8b,0x88}, {0xe7,0x19,0x5b,0x38}, {0x79,0xc8,0xee,0xdb}, +{0xa1,0x7c,0x0a,0x47}, {0x7c,0x42,0x0f,0xe9}, {0xf8,0x84,0x1e,0xc9}, {0x00,0x00,0x00,0x00}, +{0x09,0x80,0x86,0x83}, {0x32,0x2b,0xed,0x48}, {0x1e,0x11,0x70,0xac}, {0x6c,0x5a,0x72,0x4e}, +{0xfd,0x0e,0xff,0xfb}, {0x0f,0x85,0x38,0x56}, {0x3d,0xae,0xd5,0x1e}, {0x36,0x2d,0x39,0x27}, +{0x0a,0x0f,0xd9,0x64}, {0x68,0x5c,0xa6,0x21}, {0x9b,0x5b,0x54,0xd1}, {0x24,0x36,0x2e,0x3a}, +{0x0c,0x0a,0x67,0xb1}, {0x93,0x57,0xe7,0x0f}, {0xb4,0xee,0x96,0xd2}, {0x1b,0x9b,0x91,0x9e}, +{0x80,0xc0,0xc5,0x4f}, {0x61,0xdc,0x20,0xa2}, {0x5a,0x77,0x4b,0x69}, {0x1c,0x12,0x1a,0x16}, +{0xe2,0x93,0xba,0x0a}, {0xc0,0xa0,0x2a,0xe5}, {0x3c,0x22,0xe0,0x43}, {0x12,0x1b,0x17,0x1d}, +{0x0e,0x09,0x0d,0x0b}, {0xf2,0x8b,0xc7,0xad}, {0x2d,0xb6,0xa8,0xb9}, {0x14,0x1e,0xa9,0xc8}, +{0x57,0xf1,0x19,0x85}, {0xaf,0x75,0x07,0x4c}, {0xee,0x99,0xdd,0xbb}, {0xa3,0x7f,0x60,0xfd}, +{0xf7,0x01,0x26,0x9f}, {0x5c,0x72,0xf5,0xbc}, {0x44,0x66,0x3b,0xc5}, {0x5b,0xfb,0x7e,0x34}, +{0x8b,0x43,0x29,0x76}, {0xcb,0x23,0xc6,0xdc}, {0xb6,0xed,0xfc,0x68}, {0xb8,0xe4,0xf1,0x63}, +{0xd7,0x31,0xdc,0xca}, {0x42,0x63,0x85,0x10}, {0x13,0x97,0x22,0x40}, {0x84,0xc6,0x11,0x20}, +{0x85,0x4a,0x24,0x7d}, {0xd2,0xbb,0x3d,0xf8}, {0xae,0xf9,0x32,0x11}, {0xc7,0x29,0xa1,0x6d}, +{0x1d,0x9e,0x2f,0x4b}, {0xdc,0xb2,0x30,0xf3}, {0x0d,0x86,0x52,0xec}, {0x77,0xc1,0xe3,0xd0}, +{0x2b,0xb3,0x16,0x6c}, {0xa9,0x70,0xb9,0x99}, {0x11,0x94,0x48,0xfa}, {0x47,0xe9,0x64,0x22}, +{0xa8,0xfc,0x8c,0xc4}, {0xa0,0xf0,0x3f,0x1a}, {0x56,0x7d,0x2c,0xd8}, {0x22,0x33,0x90,0xef}, +{0x87,0x49,0x4e,0xc7}, {0xd9,0x38,0xd1,0xc1}, {0x8c,0xca,0xa2,0xfe}, {0x98,0xd4,0x0b,0x36}, +{0xa6,0xf5,0x81,0xcf}, {0xa5,0x7a,0xde,0x28}, {0xda,0xb7,0x8e,0x26}, {0x3f,0xad,0xbf,0xa4}, +{0x2c,0x3a,0x9d,0xe4}, {0x50,0x78,0x92,0x0d}, {0x6a,0x5f,0xcc,0x9b}, {0x54,0x7e,0x46,0x62}, +{0xf6,0x8d,0x13,0xc2}, {0x90,0xd8,0xb8,0xe8}, {0x2e,0x39,0xf7,0x5e}, {0x82,0xc3,0xaf,0xf5}, +{0x9f,0x5d,0x80,0xbe}, {0x69,0xd0,0x93,0x7c}, {0x6f,0xd5,0x2d,0xa9}, {0xcf,0x25,0x12,0xb3}, +{0xc8,0xac,0x99,0x3b}, {0x10,0x18,0x7d,0xa7}, {0xe8,0x9c,0x63,0x6e}, {0xdb,0x3b,0xbb,0x7b}, +{0xcd,0x26,0x78,0x09}, {0x6e,0x59,0x18,0xf4}, {0xec,0x9a,0xb7,0x01}, {0x83,0x4f,0x9a,0xa8}, +{0xe6,0x95,0x6e,0x65}, {0xaa,0xff,0xe6,0x7e}, {0x21,0xbc,0xcf,0x08}, {0xef,0x15,0xe8,0xe6}, +{0xba,0xe7,0x9b,0xd9}, {0x4a,0x6f,0x36,0xce}, {0xea,0x9f,0x09,0xd4}, {0x29,0xb0,0x7c,0xd6}, +{0x31,0xa4,0xb2,0xaf}, {0x2a,0x3f,0x23,0x31}, {0xc6,0xa5,0x94,0x30}, {0x35,0xa2,0x66,0xc0}, +{0x74,0x4e,0xbc,0x37}, {0xfc,0x82,0xca,0xa6}, {0xe0,0x90,0xd0,0xb0}, {0x33,0xa7,0xd8,0x15}, +{0xf1,0x04,0x98,0x4a}, {0x41,0xec,0xda,0xf7}, {0x7f,0xcd,0x50,0x0e}, {0x17,0x91,0xf6,0x2f}, +{0x76,0x4d,0xd6,0x8d}, {0x43,0xef,0xb0,0x4d}, {0xcc,0xaa,0x4d,0x54}, {0xe4,0x96,0x04,0xdf}, +{0x9e,0xd1,0xb5,0xe3}, {0x4c,0x6a,0x88,0x1b}, {0xc1,0x2c,0x1f,0xb8}, {0x46,0x65,0x51,0x7f}, +{0x9d,0x5e,0xea,0x04}, {0x01,0x8c,0x35,0x5d}, {0xfa,0x87,0x74,0x73}, {0xfb,0x0b,0x41,0x2e}, +{0xb3,0x67,0x1d,0x5a}, {0x92,0xdb,0xd2,0x52}, {0xe9,0x10,0x56,0x33}, {0x6d,0xd6,0x47,0x13}, +{0x9a,0xd7,0x61,0x8c}, {0x37,0xa1,0x0c,0x7a}, {0x59,0xf8,0x14,0x8e}, {0xeb,0x13,0x3c,0x89}, +{0xce,0xa9,0x27,0xee}, {0xb7,0x61,0xc9,0x35}, {0xe1,0x1c,0xe5,0xed}, {0x7a,0x47,0xb1,0x3c}, +{0x9c,0xd2,0xdf,0x59}, {0x55,0xf2,0x73,0x3f}, {0x18,0x14,0xce,0x79}, {0x73,0xc7,0x37,0xbf}, +{0x53,0xf7,0xcd,0xea}, {0x5f,0xfd,0xaa,0x5b}, {0xdf,0x3d,0x6f,0x14}, {0x78,0x44,0xdb,0x86}, +{0xca,0xaf,0xf3,0x81}, {0xb9,0x68,0xc4,0x3e}, {0x38,0x24,0x34,0x2c}, {0xc2,0xa3,0x40,0x5f}, +{0x16,0x1d,0xc3,0x72}, {0xbc,0xe2,0x25,0x0c}, {0x28,0x3c,0x49,0x8b}, {0xff,0x0d,0x95,0x41}, +{0x39,0xa8,0x01,0x71}, {0x08,0x0c,0xb3,0xde}, {0xd8,0xb4,0xe4,0x9c}, {0x64,0x56,0xc1,0x90}, +{0x7b,0xcb,0x84,0x61}, {0xd5,0x32,0xb6,0x70}, {0x48,0x6c,0x5c,0x74}, {0xd0,0xb8,0x57,0x42} +}; + +word8 T6[256][4] = { +{0x50,0x51,0xf4,0xa7}, {0x53,0x7e,0x41,0x65}, {0xc3,0x1a,0x17,0xa4}, {0x96,0x3a,0x27,0x5e}, +{0xcb,0x3b,0xab,0x6b}, {0xf1,0x1f,0x9d,0x45}, {0xab,0xac,0xfa,0x58}, {0x93,0x4b,0xe3,0x03}, +{0x55,0x20,0x30,0xfa}, {0xf6,0xad,0x76,0x6d}, {0x91,0x88,0xcc,0x76}, {0x25,0xf5,0x02,0x4c}, +{0xfc,0x4f,0xe5,0xd7}, {0xd7,0xc5,0x2a,0xcb}, {0x80,0x26,0x35,0x44}, {0x8f,0xb5,0x62,0xa3}, +{0x49,0xde,0xb1,0x5a}, {0x67,0x25,0xba,0x1b}, {0x98,0x45,0xea,0x0e}, {0xe1,0x5d,0xfe,0xc0}, +{0x02,0xc3,0x2f,0x75}, {0x12,0x81,0x4c,0xf0}, {0xa3,0x8d,0x46,0x97}, {0xc6,0x6b,0xd3,0xf9}, +{0xe7,0x03,0x8f,0x5f}, {0x95,0x15,0x92,0x9c}, {0xeb,0xbf,0x6d,0x7a}, {0xda,0x95,0x52,0x59}, +{0x2d,0xd4,0xbe,0x83}, {0xd3,0x58,0x74,0x21}, {0x29,0x49,0xe0,0x69}, {0x44,0x8e,0xc9,0xc8}, +{0x6a,0x75,0xc2,0x89}, {0x78,0xf4,0x8e,0x79}, {0x6b,0x99,0x58,0x3e}, {0xdd,0x27,0xb9,0x71}, +{0xb6,0xbe,0xe1,0x4f}, {0x17,0xf0,0x88,0xad}, {0x66,0xc9,0x20,0xac}, {0xb4,0x7d,0xce,0x3a}, +{0x18,0x63,0xdf,0x4a}, {0x82,0xe5,0x1a,0x31}, {0x60,0x97,0x51,0x33}, {0x45,0x62,0x53,0x7f}, +{0xe0,0xb1,0x64,0x77}, {0x84,0xbb,0x6b,0xae}, {0x1c,0xfe,0x81,0xa0}, {0x94,0xf9,0x08,0x2b}, +{0x58,0x70,0x48,0x68}, {0x19,0x8f,0x45,0xfd}, {0x87,0x94,0xde,0x6c}, {0xb7,0x52,0x7b,0xf8}, +{0x23,0xab,0x73,0xd3}, {0xe2,0x72,0x4b,0x02}, {0x57,0xe3,0x1f,0x8f}, {0x2a,0x66,0x55,0xab}, +{0x07,0xb2,0xeb,0x28}, {0x03,0x2f,0xb5,0xc2}, {0x9a,0x86,0xc5,0x7b}, {0xa5,0xd3,0x37,0x08}, +{0xf2,0x30,0x28,0x87}, {0xb2,0x23,0xbf,0xa5}, {0xba,0x02,0x03,0x6a}, {0x5c,0xed,0x16,0x82}, +{0x2b,0x8a,0xcf,0x1c}, {0x92,0xa7,0x79,0xb4}, {0xf0,0xf3,0x07,0xf2}, {0xa1,0x4e,0x69,0xe2}, +{0xcd,0x65,0xda,0xf4}, {0xd5,0x06,0x05,0xbe}, {0x1f,0xd1,0x34,0x62}, {0x8a,0xc4,0xa6,0xfe}, +{0x9d,0x34,0x2e,0x53}, {0xa0,0xa2,0xf3,0x55}, {0x32,0x05,0x8a,0xe1}, {0x75,0xa4,0xf6,0xeb}, +{0x39,0x0b,0x83,0xec}, {0xaa,0x40,0x60,0xef}, {0x06,0x5e,0x71,0x9f}, {0x51,0xbd,0x6e,0x10}, +{0xf9,0x3e,0x21,0x8a}, {0x3d,0x96,0xdd,0x06}, {0xae,0xdd,0x3e,0x05}, {0x46,0x4d,0xe6,0xbd}, +{0xb5,0x91,0x54,0x8d}, {0x05,0x71,0xc4,0x5d}, {0x6f,0x04,0x06,0xd4}, {0xff,0x60,0x50,0x15}, +{0x24,0x19,0x98,0xfb}, {0x97,0xd6,0xbd,0xe9}, {0xcc,0x89,0x40,0x43}, {0x77,0x67,0xd9,0x9e}, +{0xbd,0xb0,0xe8,0x42}, {0x88,0x07,0x89,0x8b}, {0x38,0xe7,0x19,0x5b}, {0xdb,0x79,0xc8,0xee}, +{0x47,0xa1,0x7c,0x0a}, {0xe9,0x7c,0x42,0x0f}, {0xc9,0xf8,0x84,0x1e}, {0x00,0x00,0x00,0x00}, +{0x83,0x09,0x80,0x86}, {0x48,0x32,0x2b,0xed}, {0xac,0x1e,0x11,0x70}, {0x4e,0x6c,0x5a,0x72}, +{0xfb,0xfd,0x0e,0xff}, {0x56,0x0f,0x85,0x38}, {0x1e,0x3d,0xae,0xd5}, {0x27,0x36,0x2d,0x39}, +{0x64,0x0a,0x0f,0xd9}, {0x21,0x68,0x5c,0xa6}, {0xd1,0x9b,0x5b,0x54}, {0x3a,0x24,0x36,0x2e}, +{0xb1,0x0c,0x0a,0x67}, {0x0f,0x93,0x57,0xe7}, {0xd2,0xb4,0xee,0x96}, {0x9e,0x1b,0x9b,0x91}, +{0x4f,0x80,0xc0,0xc5}, {0xa2,0x61,0xdc,0x20}, {0x69,0x5a,0x77,0x4b}, {0x16,0x1c,0x12,0x1a}, +{0x0a,0xe2,0x93,0xba}, {0xe5,0xc0,0xa0,0x2a}, {0x43,0x3c,0x22,0xe0}, {0x1d,0x12,0x1b,0x17}, +{0x0b,0x0e,0x09,0x0d}, {0xad,0xf2,0x8b,0xc7}, {0xb9,0x2d,0xb6,0xa8}, {0xc8,0x14,0x1e,0xa9}, +{0x85,0x57,0xf1,0x19}, {0x4c,0xaf,0x75,0x07}, {0xbb,0xee,0x99,0xdd}, {0xfd,0xa3,0x7f,0x60}, +{0x9f,0xf7,0x01,0x26}, {0xbc,0x5c,0x72,0xf5}, {0xc5,0x44,0x66,0x3b}, {0x34,0x5b,0xfb,0x7e}, +{0x76,0x8b,0x43,0x29}, {0xdc,0xcb,0x23,0xc6}, {0x68,0xb6,0xed,0xfc}, {0x63,0xb8,0xe4,0xf1}, +{0xca,0xd7,0x31,0xdc}, {0x10,0x42,0x63,0x85}, {0x40,0x13,0x97,0x22}, {0x20,0x84,0xc6,0x11}, +{0x7d,0x85,0x4a,0x24}, {0xf8,0xd2,0xbb,0x3d}, {0x11,0xae,0xf9,0x32}, {0x6d,0xc7,0x29,0xa1}, +{0x4b,0x1d,0x9e,0x2f}, {0xf3,0xdc,0xb2,0x30}, {0xec,0x0d,0x86,0x52}, {0xd0,0x77,0xc1,0xe3}, +{0x6c,0x2b,0xb3,0x16}, {0x99,0xa9,0x70,0xb9}, {0xfa,0x11,0x94,0x48}, {0x22,0x47,0xe9,0x64}, +{0xc4,0xa8,0xfc,0x8c}, {0x1a,0xa0,0xf0,0x3f}, {0xd8,0x56,0x7d,0x2c}, {0xef,0x22,0x33,0x90}, +{0xc7,0x87,0x49,0x4e}, {0xc1,0xd9,0x38,0xd1}, {0xfe,0x8c,0xca,0xa2}, {0x36,0x98,0xd4,0x0b}, +{0xcf,0xa6,0xf5,0x81}, {0x28,0xa5,0x7a,0xde}, {0x26,0xda,0xb7,0x8e}, {0xa4,0x3f,0xad,0xbf}, +{0xe4,0x2c,0x3a,0x9d}, {0x0d,0x50,0x78,0x92}, {0x9b,0x6a,0x5f,0xcc}, {0x62,0x54,0x7e,0x46}, +{0xc2,0xf6,0x8d,0x13}, {0xe8,0x90,0xd8,0xb8}, {0x5e,0x2e,0x39,0xf7}, {0xf5,0x82,0xc3,0xaf}, +{0xbe,0x9f,0x5d,0x80}, {0x7c,0x69,0xd0,0x93}, {0xa9,0x6f,0xd5,0x2d}, {0xb3,0xcf,0x25,0x12}, +{0x3b,0xc8,0xac,0x99}, {0xa7,0x10,0x18,0x7d}, {0x6e,0xe8,0x9c,0x63}, {0x7b,0xdb,0x3b,0xbb}, +{0x09,0xcd,0x26,0x78}, {0xf4,0x6e,0x59,0x18}, {0x01,0xec,0x9a,0xb7}, {0xa8,0x83,0x4f,0x9a}, +{0x65,0xe6,0x95,0x6e}, {0x7e,0xaa,0xff,0xe6}, {0x08,0x21,0xbc,0xcf}, {0xe6,0xef,0x15,0xe8}, +{0xd9,0xba,0xe7,0x9b}, {0xce,0x4a,0x6f,0x36}, {0xd4,0xea,0x9f,0x09}, {0xd6,0x29,0xb0,0x7c}, +{0xaf,0x31,0xa4,0xb2}, {0x31,0x2a,0x3f,0x23}, {0x30,0xc6,0xa5,0x94}, {0xc0,0x35,0xa2,0x66}, +{0x37,0x74,0x4e,0xbc}, {0xa6,0xfc,0x82,0xca}, {0xb0,0xe0,0x90,0xd0}, {0x15,0x33,0xa7,0xd8}, +{0x4a,0xf1,0x04,0x98}, {0xf7,0x41,0xec,0xda}, {0x0e,0x7f,0xcd,0x50}, {0x2f,0x17,0x91,0xf6}, +{0x8d,0x76,0x4d,0xd6}, {0x4d,0x43,0xef,0xb0}, {0x54,0xcc,0xaa,0x4d}, {0xdf,0xe4,0x96,0x04}, +{0xe3,0x9e,0xd1,0xb5}, {0x1b,0x4c,0x6a,0x88}, {0xb8,0xc1,0x2c,0x1f}, {0x7f,0x46,0x65,0x51}, +{0x04,0x9d,0x5e,0xea}, {0x5d,0x01,0x8c,0x35}, {0x73,0xfa,0x87,0x74}, {0x2e,0xfb,0x0b,0x41}, +{0x5a,0xb3,0x67,0x1d}, {0x52,0x92,0xdb,0xd2}, {0x33,0xe9,0x10,0x56}, {0x13,0x6d,0xd6,0x47}, +{0x8c,0x9a,0xd7,0x61}, {0x7a,0x37,0xa1,0x0c}, {0x8e,0x59,0xf8,0x14}, {0x89,0xeb,0x13,0x3c}, +{0xee,0xce,0xa9,0x27}, {0x35,0xb7,0x61,0xc9}, {0xed,0xe1,0x1c,0xe5}, {0x3c,0x7a,0x47,0xb1}, +{0x59,0x9c,0xd2,0xdf}, {0x3f,0x55,0xf2,0x73}, {0x79,0x18,0x14,0xce}, {0xbf,0x73,0xc7,0x37}, +{0xea,0x53,0xf7,0xcd}, {0x5b,0x5f,0xfd,0xaa}, {0x14,0xdf,0x3d,0x6f}, {0x86,0x78,0x44,0xdb}, +{0x81,0xca,0xaf,0xf3}, {0x3e,0xb9,0x68,0xc4}, {0x2c,0x38,0x24,0x34}, {0x5f,0xc2,0xa3,0x40}, +{0x72,0x16,0x1d,0xc3}, {0x0c,0xbc,0xe2,0x25}, {0x8b,0x28,0x3c,0x49}, {0x41,0xff,0x0d,0x95}, +{0x71,0x39,0xa8,0x01}, {0xde,0x08,0x0c,0xb3}, {0x9c,0xd8,0xb4,0xe4}, {0x90,0x64,0x56,0xc1}, +{0x61,0x7b,0xcb,0x84}, {0x70,0xd5,0x32,0xb6}, {0x74,0x48,0x6c,0x5c}, {0x42,0xd0,0xb8,0x57} +}; + +word8 T7[256][4] = { +{0xa7,0x50,0x51,0xf4}, {0x65,0x53,0x7e,0x41}, {0xa4,0xc3,0x1a,0x17}, {0x5e,0x96,0x3a,0x27}, +{0x6b,0xcb,0x3b,0xab}, {0x45,0xf1,0x1f,0x9d}, {0x58,0xab,0xac,0xfa}, {0x03,0x93,0x4b,0xe3}, +{0xfa,0x55,0x20,0x30}, {0x6d,0xf6,0xad,0x76}, {0x76,0x91,0x88,0xcc}, {0x4c,0x25,0xf5,0x02}, +{0xd7,0xfc,0x4f,0xe5}, {0xcb,0xd7,0xc5,0x2a}, {0x44,0x80,0x26,0x35}, {0xa3,0x8f,0xb5,0x62}, +{0x5a,0x49,0xde,0xb1}, {0x1b,0x67,0x25,0xba}, {0x0e,0x98,0x45,0xea}, {0xc0,0xe1,0x5d,0xfe}, +{0x75,0x02,0xc3,0x2f}, {0xf0,0x12,0x81,0x4c}, {0x97,0xa3,0x8d,0x46}, {0xf9,0xc6,0x6b,0xd3}, +{0x5f,0xe7,0x03,0x8f}, {0x9c,0x95,0x15,0x92}, {0x7a,0xeb,0xbf,0x6d}, {0x59,0xda,0x95,0x52}, +{0x83,0x2d,0xd4,0xbe}, {0x21,0xd3,0x58,0x74}, {0x69,0x29,0x49,0xe0}, {0xc8,0x44,0x8e,0xc9}, +{0x89,0x6a,0x75,0xc2}, {0x79,0x78,0xf4,0x8e}, {0x3e,0x6b,0x99,0x58}, {0x71,0xdd,0x27,0xb9}, +{0x4f,0xb6,0xbe,0xe1}, {0xad,0x17,0xf0,0x88}, {0xac,0x66,0xc9,0x20}, {0x3a,0xb4,0x7d,0xce}, +{0x4a,0x18,0x63,0xdf}, {0x31,0x82,0xe5,0x1a}, {0x33,0x60,0x97,0x51}, {0x7f,0x45,0x62,0x53}, +{0x77,0xe0,0xb1,0x64}, {0xae,0x84,0xbb,0x6b}, {0xa0,0x1c,0xfe,0x81}, {0x2b,0x94,0xf9,0x08}, +{0x68,0x58,0x70,0x48}, {0xfd,0x19,0x8f,0x45}, {0x6c,0x87,0x94,0xde}, {0xf8,0xb7,0x52,0x7b}, +{0xd3,0x23,0xab,0x73}, {0x02,0xe2,0x72,0x4b}, {0x8f,0x57,0xe3,0x1f}, {0xab,0x2a,0x66,0x55}, +{0x28,0x07,0xb2,0xeb}, {0xc2,0x03,0x2f,0xb5}, {0x7b,0x9a,0x86,0xc5}, {0x08,0xa5,0xd3,0x37}, +{0x87,0xf2,0x30,0x28}, {0xa5,0xb2,0x23,0xbf}, {0x6a,0xba,0x02,0x03}, {0x82,0x5c,0xed,0x16}, +{0x1c,0x2b,0x8a,0xcf}, {0xb4,0x92,0xa7,0x79}, {0xf2,0xf0,0xf3,0x07}, {0xe2,0xa1,0x4e,0x69}, +{0xf4,0xcd,0x65,0xda}, {0xbe,0xd5,0x06,0x05}, {0x62,0x1f,0xd1,0x34}, {0xfe,0x8a,0xc4,0xa6}, +{0x53,0x9d,0x34,0x2e}, {0x55,0xa0,0xa2,0xf3}, {0xe1,0x32,0x05,0x8a}, {0xeb,0x75,0xa4,0xf6}, +{0xec,0x39,0x0b,0x83}, {0xef,0xaa,0x40,0x60}, {0x9f,0x06,0x5e,0x71}, {0x10,0x51,0xbd,0x6e}, +{0x8a,0xf9,0x3e,0x21}, {0x06,0x3d,0x96,0xdd}, {0x05,0xae,0xdd,0x3e}, {0xbd,0x46,0x4d,0xe6}, +{0x8d,0xb5,0x91,0x54}, {0x5d,0x05,0x71,0xc4}, {0xd4,0x6f,0x04,0x06}, {0x15,0xff,0x60,0x50}, +{0xfb,0x24,0x19,0x98}, {0xe9,0x97,0xd6,0xbd}, {0x43,0xcc,0x89,0x40}, {0x9e,0x77,0x67,0xd9}, +{0x42,0xbd,0xb0,0xe8}, {0x8b,0x88,0x07,0x89}, {0x5b,0x38,0xe7,0x19}, {0xee,0xdb,0x79,0xc8}, +{0x0a,0x47,0xa1,0x7c}, {0x0f,0xe9,0x7c,0x42}, {0x1e,0xc9,0xf8,0x84}, {0x00,0x00,0x00,0x00}, +{0x86,0x83,0x09,0x80}, {0xed,0x48,0x32,0x2b}, {0x70,0xac,0x1e,0x11}, {0x72,0x4e,0x6c,0x5a}, +{0xff,0xfb,0xfd,0x0e}, {0x38,0x56,0x0f,0x85}, {0xd5,0x1e,0x3d,0xae}, {0x39,0x27,0x36,0x2d}, +{0xd9,0x64,0x0a,0x0f}, {0xa6,0x21,0x68,0x5c}, {0x54,0xd1,0x9b,0x5b}, {0x2e,0x3a,0x24,0x36}, +{0x67,0xb1,0x0c,0x0a}, {0xe7,0x0f,0x93,0x57}, {0x96,0xd2,0xb4,0xee}, {0x91,0x9e,0x1b,0x9b}, +{0xc5,0x4f,0x80,0xc0}, {0x20,0xa2,0x61,0xdc}, {0x4b,0x69,0x5a,0x77}, {0x1a,0x16,0x1c,0x12}, +{0xba,0x0a,0xe2,0x93}, {0x2a,0xe5,0xc0,0xa0}, {0xe0,0x43,0x3c,0x22}, {0x17,0x1d,0x12,0x1b}, +{0x0d,0x0b,0x0e,0x09}, {0xc7,0xad,0xf2,0x8b}, {0xa8,0xb9,0x2d,0xb6}, {0xa9,0xc8,0x14,0x1e}, +{0x19,0x85,0x57,0xf1}, {0x07,0x4c,0xaf,0x75}, {0xdd,0xbb,0xee,0x99}, {0x60,0xfd,0xa3,0x7f}, +{0x26,0x9f,0xf7,0x01}, {0xf5,0xbc,0x5c,0x72}, {0x3b,0xc5,0x44,0x66}, {0x7e,0x34,0x5b,0xfb}, +{0x29,0x76,0x8b,0x43}, {0xc6,0xdc,0xcb,0x23}, {0xfc,0x68,0xb6,0xed}, {0xf1,0x63,0xb8,0xe4}, +{0xdc,0xca,0xd7,0x31}, {0x85,0x10,0x42,0x63}, {0x22,0x40,0x13,0x97}, {0x11,0x20,0x84,0xc6}, +{0x24,0x7d,0x85,0x4a}, {0x3d,0xf8,0xd2,0xbb}, {0x32,0x11,0xae,0xf9}, {0xa1,0x6d,0xc7,0x29}, +{0x2f,0x4b,0x1d,0x9e}, {0x30,0xf3,0xdc,0xb2}, {0x52,0xec,0x0d,0x86}, {0xe3,0xd0,0x77,0xc1}, +{0x16,0x6c,0x2b,0xb3}, {0xb9,0x99,0xa9,0x70}, {0x48,0xfa,0x11,0x94}, {0x64,0x22,0x47,0xe9}, +{0x8c,0xc4,0xa8,0xfc}, {0x3f,0x1a,0xa0,0xf0}, {0x2c,0xd8,0x56,0x7d}, {0x90,0xef,0x22,0x33}, +{0x4e,0xc7,0x87,0x49}, {0xd1,0xc1,0xd9,0x38}, {0xa2,0xfe,0x8c,0xca}, {0x0b,0x36,0x98,0xd4}, +{0x81,0xcf,0xa6,0xf5}, {0xde,0x28,0xa5,0x7a}, {0x8e,0x26,0xda,0xb7}, {0xbf,0xa4,0x3f,0xad}, +{0x9d,0xe4,0x2c,0x3a}, {0x92,0x0d,0x50,0x78}, {0xcc,0x9b,0x6a,0x5f}, {0x46,0x62,0x54,0x7e}, +{0x13,0xc2,0xf6,0x8d}, {0xb8,0xe8,0x90,0xd8}, {0xf7,0x5e,0x2e,0x39}, {0xaf,0xf5,0x82,0xc3}, +{0x80,0xbe,0x9f,0x5d}, {0x93,0x7c,0x69,0xd0}, {0x2d,0xa9,0x6f,0xd5}, {0x12,0xb3,0xcf,0x25}, +{0x99,0x3b,0xc8,0xac}, {0x7d,0xa7,0x10,0x18}, {0x63,0x6e,0xe8,0x9c}, {0xbb,0x7b,0xdb,0x3b}, +{0x78,0x09,0xcd,0x26}, {0x18,0xf4,0x6e,0x59}, {0xb7,0x01,0xec,0x9a}, {0x9a,0xa8,0x83,0x4f}, +{0x6e,0x65,0xe6,0x95}, {0xe6,0x7e,0xaa,0xff}, {0xcf,0x08,0x21,0xbc}, {0xe8,0xe6,0xef,0x15}, +{0x9b,0xd9,0xba,0xe7}, {0x36,0xce,0x4a,0x6f}, {0x09,0xd4,0xea,0x9f}, {0x7c,0xd6,0x29,0xb0}, +{0xb2,0xaf,0x31,0xa4}, {0x23,0x31,0x2a,0x3f}, {0x94,0x30,0xc6,0xa5}, {0x66,0xc0,0x35,0xa2}, +{0xbc,0x37,0x74,0x4e}, {0xca,0xa6,0xfc,0x82}, {0xd0,0xb0,0xe0,0x90}, {0xd8,0x15,0x33,0xa7}, +{0x98,0x4a,0xf1,0x04}, {0xda,0xf7,0x41,0xec}, {0x50,0x0e,0x7f,0xcd}, {0xf6,0x2f,0x17,0x91}, +{0xd6,0x8d,0x76,0x4d}, {0xb0,0x4d,0x43,0xef}, {0x4d,0x54,0xcc,0xaa}, {0x04,0xdf,0xe4,0x96}, +{0xb5,0xe3,0x9e,0xd1}, {0x88,0x1b,0x4c,0x6a}, {0x1f,0xb8,0xc1,0x2c}, {0x51,0x7f,0x46,0x65}, +{0xea,0x04,0x9d,0x5e}, {0x35,0x5d,0x01,0x8c}, {0x74,0x73,0xfa,0x87}, {0x41,0x2e,0xfb,0x0b}, +{0x1d,0x5a,0xb3,0x67}, {0xd2,0x52,0x92,0xdb}, {0x56,0x33,0xe9,0x10}, {0x47,0x13,0x6d,0xd6}, +{0x61,0x8c,0x9a,0xd7}, {0x0c,0x7a,0x37,0xa1}, {0x14,0x8e,0x59,0xf8}, {0x3c,0x89,0xeb,0x13}, +{0x27,0xee,0xce,0xa9}, {0xc9,0x35,0xb7,0x61}, {0xe5,0xed,0xe1,0x1c}, {0xb1,0x3c,0x7a,0x47}, +{0xdf,0x59,0x9c,0xd2}, {0x73,0x3f,0x55,0xf2}, {0xce,0x79,0x18,0x14}, {0x37,0xbf,0x73,0xc7}, +{0xcd,0xea,0x53,0xf7}, {0xaa,0x5b,0x5f,0xfd}, {0x6f,0x14,0xdf,0x3d}, {0xdb,0x86,0x78,0x44}, +{0xf3,0x81,0xca,0xaf}, {0xc4,0x3e,0xb9,0x68}, {0x34,0x2c,0x38,0x24}, {0x40,0x5f,0xc2,0xa3}, +{0xc3,0x72,0x16,0x1d}, {0x25,0x0c,0xbc,0xe2}, {0x49,0x8b,0x28,0x3c}, {0x95,0x41,0xff,0x0d}, +{0x01,0x71,0x39,0xa8}, {0xb3,0xde,0x08,0x0c}, {0xe4,0x9c,0xd8,0xb4}, {0xc1,0x90,0x64,0x56}, +{0x84,0x61,0x7b,0xcb}, {0xb6,0x70,0xd5,0x32}, {0x5c,0x74,0x48,0x6c}, {0x57,0x42,0xd0,0xb8} +}; + +word8 T8[256][4] = { +{0xf4,0xa7,0x50,0x51}, {0x41,0x65,0x53,0x7e}, {0x17,0xa4,0xc3,0x1a}, {0x27,0x5e,0x96,0x3a}, +{0xab,0x6b,0xcb,0x3b}, {0x9d,0x45,0xf1,0x1f}, {0xfa,0x58,0xab,0xac}, {0xe3,0x03,0x93,0x4b}, +{0x30,0xfa,0x55,0x20}, {0x76,0x6d,0xf6,0xad}, {0xcc,0x76,0x91,0x88}, {0x02,0x4c,0x25,0xf5}, +{0xe5,0xd7,0xfc,0x4f}, {0x2a,0xcb,0xd7,0xc5}, {0x35,0x44,0x80,0x26}, {0x62,0xa3,0x8f,0xb5}, +{0xb1,0x5a,0x49,0xde}, {0xba,0x1b,0x67,0x25}, {0xea,0x0e,0x98,0x45}, {0xfe,0xc0,0xe1,0x5d}, +{0x2f,0x75,0x02,0xc3}, {0x4c,0xf0,0x12,0x81}, {0x46,0x97,0xa3,0x8d}, {0xd3,0xf9,0xc6,0x6b}, +{0x8f,0x5f,0xe7,0x03}, {0x92,0x9c,0x95,0x15}, {0x6d,0x7a,0xeb,0xbf}, {0x52,0x59,0xda,0x95}, +{0xbe,0x83,0x2d,0xd4}, {0x74,0x21,0xd3,0x58}, {0xe0,0x69,0x29,0x49}, {0xc9,0xc8,0x44,0x8e}, +{0xc2,0x89,0x6a,0x75}, {0x8e,0x79,0x78,0xf4}, {0x58,0x3e,0x6b,0x99}, {0xb9,0x71,0xdd,0x27}, +{0xe1,0x4f,0xb6,0xbe}, {0x88,0xad,0x17,0xf0}, {0x20,0xac,0x66,0xc9}, {0xce,0x3a,0xb4,0x7d}, +{0xdf,0x4a,0x18,0x63}, {0x1a,0x31,0x82,0xe5}, {0x51,0x33,0x60,0x97}, {0x53,0x7f,0x45,0x62}, +{0x64,0x77,0xe0,0xb1}, {0x6b,0xae,0x84,0xbb}, {0x81,0xa0,0x1c,0xfe}, {0x08,0x2b,0x94,0xf9}, +{0x48,0x68,0x58,0x70}, {0x45,0xfd,0x19,0x8f}, {0xde,0x6c,0x87,0x94}, {0x7b,0xf8,0xb7,0x52}, +{0x73,0xd3,0x23,0xab}, {0x4b,0x02,0xe2,0x72}, {0x1f,0x8f,0x57,0xe3}, {0x55,0xab,0x2a,0x66}, +{0xeb,0x28,0x07,0xb2}, {0xb5,0xc2,0x03,0x2f}, {0xc5,0x7b,0x9a,0x86}, {0x37,0x08,0xa5,0xd3}, +{0x28,0x87,0xf2,0x30}, {0xbf,0xa5,0xb2,0x23}, {0x03,0x6a,0xba,0x02}, {0x16,0x82,0x5c,0xed}, +{0xcf,0x1c,0x2b,0x8a}, {0x79,0xb4,0x92,0xa7}, {0x07,0xf2,0xf0,0xf3}, {0x69,0xe2,0xa1,0x4e}, +{0xda,0xf4,0xcd,0x65}, {0x05,0xbe,0xd5,0x06}, {0x34,0x62,0x1f,0xd1}, {0xa6,0xfe,0x8a,0xc4}, +{0x2e,0x53,0x9d,0x34}, {0xf3,0x55,0xa0,0xa2}, {0x8a,0xe1,0x32,0x05}, {0xf6,0xeb,0x75,0xa4}, +{0x83,0xec,0x39,0x0b}, {0x60,0xef,0xaa,0x40}, {0x71,0x9f,0x06,0x5e}, {0x6e,0x10,0x51,0xbd}, +{0x21,0x8a,0xf9,0x3e}, {0xdd,0x06,0x3d,0x96}, {0x3e,0x05,0xae,0xdd}, {0xe6,0xbd,0x46,0x4d}, +{0x54,0x8d,0xb5,0x91}, {0xc4,0x5d,0x05,0x71}, {0x06,0xd4,0x6f,0x04}, {0x50,0x15,0xff,0x60}, +{0x98,0xfb,0x24,0x19}, {0xbd,0xe9,0x97,0xd6}, {0x40,0x43,0xcc,0x89}, {0xd9,0x9e,0x77,0x67}, +{0xe8,0x42,0xbd,0xb0}, {0x89,0x8b,0x88,0x07}, {0x19,0x5b,0x38,0xe7}, {0xc8,0xee,0xdb,0x79}, +{0x7c,0x0a,0x47,0xa1}, {0x42,0x0f,0xe9,0x7c}, {0x84,0x1e,0xc9,0xf8}, {0x00,0x00,0x00,0x00}, +{0x80,0x86,0x83,0x09}, {0x2b,0xed,0x48,0x32}, {0x11,0x70,0xac,0x1e}, {0x5a,0x72,0x4e,0x6c}, +{0x0e,0xff,0xfb,0xfd}, {0x85,0x38,0x56,0x0f}, {0xae,0xd5,0x1e,0x3d}, {0x2d,0x39,0x27,0x36}, +{0x0f,0xd9,0x64,0x0a}, {0x5c,0xa6,0x21,0x68}, {0x5b,0x54,0xd1,0x9b}, {0x36,0x2e,0x3a,0x24}, +{0x0a,0x67,0xb1,0x0c}, {0x57,0xe7,0x0f,0x93}, {0xee,0x96,0xd2,0xb4}, {0x9b,0x91,0x9e,0x1b}, +{0xc0,0xc5,0x4f,0x80}, {0xdc,0x20,0xa2,0x61}, {0x77,0x4b,0x69,0x5a}, {0x12,0x1a,0x16,0x1c}, +{0x93,0xba,0x0a,0xe2}, {0xa0,0x2a,0xe5,0xc0}, {0x22,0xe0,0x43,0x3c}, {0x1b,0x17,0x1d,0x12}, +{0x09,0x0d,0x0b,0x0e}, {0x8b,0xc7,0xad,0xf2}, {0xb6,0xa8,0xb9,0x2d}, {0x1e,0xa9,0xc8,0x14}, +{0xf1,0x19,0x85,0x57}, {0x75,0x07,0x4c,0xaf}, {0x99,0xdd,0xbb,0xee}, {0x7f,0x60,0xfd,0xa3}, +{0x01,0x26,0x9f,0xf7}, {0x72,0xf5,0xbc,0x5c}, {0x66,0x3b,0xc5,0x44}, {0xfb,0x7e,0x34,0x5b}, +{0x43,0x29,0x76,0x8b}, {0x23,0xc6,0xdc,0xcb}, {0xed,0xfc,0x68,0xb6}, {0xe4,0xf1,0x63,0xb8}, +{0x31,0xdc,0xca,0xd7}, {0x63,0x85,0x10,0x42}, {0x97,0x22,0x40,0x13}, {0xc6,0x11,0x20,0x84}, +{0x4a,0x24,0x7d,0x85}, {0xbb,0x3d,0xf8,0xd2}, {0xf9,0x32,0x11,0xae}, {0x29,0xa1,0x6d,0xc7}, +{0x9e,0x2f,0x4b,0x1d}, {0xb2,0x30,0xf3,0xdc}, {0x86,0x52,0xec,0x0d}, {0xc1,0xe3,0xd0,0x77}, +{0xb3,0x16,0x6c,0x2b}, {0x70,0xb9,0x99,0xa9}, {0x94,0x48,0xfa,0x11}, {0xe9,0x64,0x22,0x47}, +{0xfc,0x8c,0xc4,0xa8}, {0xf0,0x3f,0x1a,0xa0}, {0x7d,0x2c,0xd8,0x56}, {0x33,0x90,0xef,0x22}, +{0x49,0x4e,0xc7,0x87}, {0x38,0xd1,0xc1,0xd9}, {0xca,0xa2,0xfe,0x8c}, {0xd4,0x0b,0x36,0x98}, +{0xf5,0x81,0xcf,0xa6}, {0x7a,0xde,0x28,0xa5}, {0xb7,0x8e,0x26,0xda}, {0xad,0xbf,0xa4,0x3f}, +{0x3a,0x9d,0xe4,0x2c}, {0x78,0x92,0x0d,0x50}, {0x5f,0xcc,0x9b,0x6a}, {0x7e,0x46,0x62,0x54}, +{0x8d,0x13,0xc2,0xf6}, {0xd8,0xb8,0xe8,0x90}, {0x39,0xf7,0x5e,0x2e}, {0xc3,0xaf,0xf5,0x82}, +{0x5d,0x80,0xbe,0x9f}, {0xd0,0x93,0x7c,0x69}, {0xd5,0x2d,0xa9,0x6f}, {0x25,0x12,0xb3,0xcf}, +{0xac,0x99,0x3b,0xc8}, {0x18,0x7d,0xa7,0x10}, {0x9c,0x63,0x6e,0xe8}, {0x3b,0xbb,0x7b,0xdb}, +{0x26,0x78,0x09,0xcd}, {0x59,0x18,0xf4,0x6e}, {0x9a,0xb7,0x01,0xec}, {0x4f,0x9a,0xa8,0x83}, +{0x95,0x6e,0x65,0xe6}, {0xff,0xe6,0x7e,0xaa}, {0xbc,0xcf,0x08,0x21}, {0x15,0xe8,0xe6,0xef}, +{0xe7,0x9b,0xd9,0xba}, {0x6f,0x36,0xce,0x4a}, {0x9f,0x09,0xd4,0xea}, {0xb0,0x7c,0xd6,0x29}, +{0xa4,0xb2,0xaf,0x31}, {0x3f,0x23,0x31,0x2a}, {0xa5,0x94,0x30,0xc6}, {0xa2,0x66,0xc0,0x35}, +{0x4e,0xbc,0x37,0x74}, {0x82,0xca,0xa6,0xfc}, {0x90,0xd0,0xb0,0xe0}, {0xa7,0xd8,0x15,0x33}, +{0x04,0x98,0x4a,0xf1}, {0xec,0xda,0xf7,0x41}, {0xcd,0x50,0x0e,0x7f}, {0x91,0xf6,0x2f,0x17}, +{0x4d,0xd6,0x8d,0x76}, {0xef,0xb0,0x4d,0x43}, {0xaa,0x4d,0x54,0xcc}, {0x96,0x04,0xdf,0xe4}, +{0xd1,0xb5,0xe3,0x9e}, {0x6a,0x88,0x1b,0x4c}, {0x2c,0x1f,0xb8,0xc1}, {0x65,0x51,0x7f,0x46}, +{0x5e,0xea,0x04,0x9d}, {0x8c,0x35,0x5d,0x01}, {0x87,0x74,0x73,0xfa}, {0x0b,0x41,0x2e,0xfb}, +{0x67,0x1d,0x5a,0xb3}, {0xdb,0xd2,0x52,0x92}, {0x10,0x56,0x33,0xe9}, {0xd6,0x47,0x13,0x6d}, +{0xd7,0x61,0x8c,0x9a}, {0xa1,0x0c,0x7a,0x37}, {0xf8,0x14,0x8e,0x59}, {0x13,0x3c,0x89,0xeb}, +{0xa9,0x27,0xee,0xce}, {0x61,0xc9,0x35,0xb7}, {0x1c,0xe5,0xed,0xe1}, {0x47,0xb1,0x3c,0x7a}, +{0xd2,0xdf,0x59,0x9c}, {0xf2,0x73,0x3f,0x55}, {0x14,0xce,0x79,0x18}, {0xc7,0x37,0xbf,0x73}, +{0xf7,0xcd,0xea,0x53}, {0xfd,0xaa,0x5b,0x5f}, {0x3d,0x6f,0x14,0xdf}, {0x44,0xdb,0x86,0x78}, +{0xaf,0xf3,0x81,0xca}, {0x68,0xc4,0x3e,0xb9}, {0x24,0x34,0x2c,0x38}, {0xa3,0x40,0x5f,0xc2}, +{0x1d,0xc3,0x72,0x16}, {0xe2,0x25,0x0c,0xbc}, {0x3c,0x49,0x8b,0x28}, {0x0d,0x95,0x41,0xff}, +{0xa8,0x01,0x71,0x39}, {0x0c,0xb3,0xde,0x08}, {0xb4,0xe4,0x9c,0xd8}, {0x56,0xc1,0x90,0x64}, +{0xcb,0x84,0x61,0x7b}, {0x32,0xb6,0x70,0xd5}, {0x6c,0x5c,0x74,0x48}, {0xb8,0x57,0x42,0xd0} +}; + +word8 S5[256] = { +0x52,0x09,0x6a,0xd5, +0x30,0x36,0xa5,0x38, +0xbf,0x40,0xa3,0x9e, +0x81,0xf3,0xd7,0xfb, +0x7c,0xe3,0x39,0x82, +0x9b,0x2f,0xff,0x87, +0x34,0x8e,0x43,0x44, +0xc4,0xde,0xe9,0xcb, +0x54,0x7b,0x94,0x32, +0xa6,0xc2,0x23,0x3d, +0xee,0x4c,0x95,0x0b, +0x42,0xfa,0xc3,0x4e, +0x08,0x2e,0xa1,0x66, +0x28,0xd9,0x24,0xb2, +0x76,0x5b,0xa2,0x49, +0x6d,0x8b,0xd1,0x25, +0x72,0xf8,0xf6,0x64, +0x86,0x68,0x98,0x16, +0xd4,0xa4,0x5c,0xcc, +0x5d,0x65,0xb6,0x92, +0x6c,0x70,0x48,0x50, +0xfd,0xed,0xb9,0xda, +0x5e,0x15,0x46,0x57, +0xa7,0x8d,0x9d,0x84, +0x90,0xd8,0xab,0x00, +0x8c,0xbc,0xd3,0x0a, +0xf7,0xe4,0x58,0x05, +0xb8,0xb3,0x45,0x06, +0xd0,0x2c,0x1e,0x8f, +0xca,0x3f,0x0f,0x02, +0xc1,0xaf,0xbd,0x03, +0x01,0x13,0x8a,0x6b, +0x3a,0x91,0x11,0x41, +0x4f,0x67,0xdc,0xea, +0x97,0xf2,0xcf,0xce, +0xf0,0xb4,0xe6,0x73, +0x96,0xac,0x74,0x22, +0xe7,0xad,0x35,0x85, +0xe2,0xf9,0x37,0xe8, +0x1c,0x75,0xdf,0x6e, +0x47,0xf1,0x1a,0x71, +0x1d,0x29,0xc5,0x89, +0x6f,0xb7,0x62,0x0e, +0xaa,0x18,0xbe,0x1b, +0xfc,0x56,0x3e,0x4b, +0xc6,0xd2,0x79,0x20, +0x9a,0xdb,0xc0,0xfe, +0x78,0xcd,0x5a,0xf4, +0x1f,0xdd,0xa8,0x33, +0x88,0x07,0xc7,0x31, +0xb1,0x12,0x10,0x59, +0x27,0x80,0xec,0x5f, +0x60,0x51,0x7f,0xa9, +0x19,0xb5,0x4a,0x0d, +0x2d,0xe5,0x7a,0x9f, +0x93,0xc9,0x9c,0xef, +0xa0,0xe0,0x3b,0x4d, +0xae,0x2a,0xf5,0xb0, +0xc8,0xeb,0xbb,0x3c, +0x83,0x53,0x99,0x61, +0x17,0x2b,0x04,0x7e, +0xba,0x77,0xd6,0x26, +0xe1,0x69,0x14,0x63, +0x55,0x21,0x0c,0x7d +}; + +word8 U1[256][4] = { +{0x00,0x00,0x00,0x00}, {0x0e,0x09,0x0d,0x0b}, {0x1c,0x12,0x1a,0x16}, {0x12,0x1b,0x17,0x1d}, +{0x38,0x24,0x34,0x2c}, {0x36,0x2d,0x39,0x27}, {0x24,0x36,0x2e,0x3a}, {0x2a,0x3f,0x23,0x31}, +{0x70,0x48,0x68,0x58}, {0x7e,0x41,0x65,0x53}, {0x6c,0x5a,0x72,0x4e}, {0x62,0x53,0x7f,0x45}, +{0x48,0x6c,0x5c,0x74}, {0x46,0x65,0x51,0x7f}, {0x54,0x7e,0x46,0x62}, {0x5a,0x77,0x4b,0x69}, +{0xe0,0x90,0xd0,0xb0}, {0xee,0x99,0xdd,0xbb}, {0xfc,0x82,0xca,0xa6}, {0xf2,0x8b,0xc7,0xad}, +{0xd8,0xb4,0xe4,0x9c}, {0xd6,0xbd,0xe9,0x97}, {0xc4,0xa6,0xfe,0x8a}, {0xca,0xaf,0xf3,0x81}, +{0x90,0xd8,0xb8,0xe8}, {0x9e,0xd1,0xb5,0xe3}, {0x8c,0xca,0xa2,0xfe}, {0x82,0xc3,0xaf,0xf5}, +{0xa8,0xfc,0x8c,0xc4}, {0xa6,0xf5,0x81,0xcf}, {0xb4,0xee,0x96,0xd2}, {0xba,0xe7,0x9b,0xd9}, +{0xdb,0x3b,0xbb,0x7b}, {0xd5,0x32,0xb6,0x70}, {0xc7,0x29,0xa1,0x6d}, {0xc9,0x20,0xac,0x66}, +{0xe3,0x1f,0x8f,0x57}, {0xed,0x16,0x82,0x5c}, {0xff,0x0d,0x95,0x41}, {0xf1,0x04,0x98,0x4a}, +{0xab,0x73,0xd3,0x23}, {0xa5,0x7a,0xde,0x28}, {0xb7,0x61,0xc9,0x35}, {0xb9,0x68,0xc4,0x3e}, +{0x93,0x57,0xe7,0x0f}, {0x9d,0x5e,0xea,0x04}, {0x8f,0x45,0xfd,0x19}, {0x81,0x4c,0xf0,0x12}, +{0x3b,0xab,0x6b,0xcb}, {0x35,0xa2,0x66,0xc0}, {0x27,0xb9,0x71,0xdd}, {0x29,0xb0,0x7c,0xd6}, +{0x03,0x8f,0x5f,0xe7}, {0x0d,0x86,0x52,0xec}, {0x1f,0x9d,0x45,0xf1}, {0x11,0x94,0x48,0xfa}, +{0x4b,0xe3,0x03,0x93}, {0x45,0xea,0x0e,0x98}, {0x57,0xf1,0x19,0x85}, {0x59,0xf8,0x14,0x8e}, +{0x73,0xc7,0x37,0xbf}, {0x7d,0xce,0x3a,0xb4}, {0x6f,0xd5,0x2d,0xa9}, {0x61,0xdc,0x20,0xa2}, +{0xad,0x76,0x6d,0xf6}, {0xa3,0x7f,0x60,0xfd}, {0xb1,0x64,0x77,0xe0}, {0xbf,0x6d,0x7a,0xeb}, +{0x95,0x52,0x59,0xda}, {0x9b,0x5b,0x54,0xd1}, {0x89,0x40,0x43,0xcc}, {0x87,0x49,0x4e,0xc7}, +{0xdd,0x3e,0x05,0xae}, {0xd3,0x37,0x08,0xa5}, {0xc1,0x2c,0x1f,0xb8}, {0xcf,0x25,0x12,0xb3}, +{0xe5,0x1a,0x31,0x82}, {0xeb,0x13,0x3c,0x89}, {0xf9,0x08,0x2b,0x94}, {0xf7,0x01,0x26,0x9f}, +{0x4d,0xe6,0xbd,0x46}, {0x43,0xef,0xb0,0x4d}, {0x51,0xf4,0xa7,0x50}, {0x5f,0xfd,0xaa,0x5b}, +{0x75,0xc2,0x89,0x6a}, {0x7b,0xcb,0x84,0x61}, {0x69,0xd0,0x93,0x7c}, {0x67,0xd9,0x9e,0x77}, +{0x3d,0xae,0xd5,0x1e}, {0x33,0xa7,0xd8,0x15}, {0x21,0xbc,0xcf,0x08}, {0x2f,0xb5,0xc2,0x03}, +{0x05,0x8a,0xe1,0x32}, {0x0b,0x83,0xec,0x39}, {0x19,0x98,0xfb,0x24}, {0x17,0x91,0xf6,0x2f}, +{0x76,0x4d,0xd6,0x8d}, {0x78,0x44,0xdb,0x86}, {0x6a,0x5f,0xcc,0x9b}, {0x64,0x56,0xc1,0x90}, +{0x4e,0x69,0xe2,0xa1}, {0x40,0x60,0xef,0xaa}, {0x52,0x7b,0xf8,0xb7}, {0x5c,0x72,0xf5,0xbc}, +{0x06,0x05,0xbe,0xd5}, {0x08,0x0c,0xb3,0xde}, {0x1a,0x17,0xa4,0xc3}, {0x14,0x1e,0xa9,0xc8}, +{0x3e,0x21,0x8a,0xf9}, {0x30,0x28,0x87,0xf2}, {0x22,0x33,0x90,0xef}, {0x2c,0x3a,0x9d,0xe4}, +{0x96,0xdd,0x06,0x3d}, {0x98,0xd4,0x0b,0x36}, {0x8a,0xcf,0x1c,0x2b}, {0x84,0xc6,0x11,0x20}, +{0xae,0xf9,0x32,0x11}, {0xa0,0xf0,0x3f,0x1a}, {0xb2,0xeb,0x28,0x07}, {0xbc,0xe2,0x25,0x0c}, +{0xe6,0x95,0x6e,0x65}, {0xe8,0x9c,0x63,0x6e}, {0xfa,0x87,0x74,0x73}, {0xf4,0x8e,0x79,0x78}, +{0xde,0xb1,0x5a,0x49}, {0xd0,0xb8,0x57,0x42}, {0xc2,0xa3,0x40,0x5f}, {0xcc,0xaa,0x4d,0x54}, +{0x41,0xec,0xda,0xf7}, {0x4f,0xe5,0xd7,0xfc}, {0x5d,0xfe,0xc0,0xe1}, {0x53,0xf7,0xcd,0xea}, +{0x79,0xc8,0xee,0xdb}, {0x77,0xc1,0xe3,0xd0}, {0x65,0xda,0xf4,0xcd}, {0x6b,0xd3,0xf9,0xc6}, +{0x31,0xa4,0xb2,0xaf}, {0x3f,0xad,0xbf,0xa4}, {0x2d,0xb6,0xa8,0xb9}, {0x23,0xbf,0xa5,0xb2}, +{0x09,0x80,0x86,0x83}, {0x07,0x89,0x8b,0x88}, {0x15,0x92,0x9c,0x95}, {0x1b,0x9b,0x91,0x9e}, +{0xa1,0x7c,0x0a,0x47}, {0xaf,0x75,0x07,0x4c}, {0xbd,0x6e,0x10,0x51}, {0xb3,0x67,0x1d,0x5a}, +{0x99,0x58,0x3e,0x6b}, {0x97,0x51,0x33,0x60}, {0x85,0x4a,0x24,0x7d}, {0x8b,0x43,0x29,0x76}, +{0xd1,0x34,0x62,0x1f}, {0xdf,0x3d,0x6f,0x14}, {0xcd,0x26,0x78,0x09}, {0xc3,0x2f,0x75,0x02}, +{0xe9,0x10,0x56,0x33}, {0xe7,0x19,0x5b,0x38}, {0xf5,0x02,0x4c,0x25}, {0xfb,0x0b,0x41,0x2e}, +{0x9a,0xd7,0x61,0x8c}, {0x94,0xde,0x6c,0x87}, {0x86,0xc5,0x7b,0x9a}, {0x88,0xcc,0x76,0x91}, +{0xa2,0xf3,0x55,0xa0}, {0xac,0xfa,0x58,0xab}, {0xbe,0xe1,0x4f,0xb6}, {0xb0,0xe8,0x42,0xbd}, +{0xea,0x9f,0x09,0xd4}, {0xe4,0x96,0x04,0xdf}, {0xf6,0x8d,0x13,0xc2}, {0xf8,0x84,0x1e,0xc9}, +{0xd2,0xbb,0x3d,0xf8}, {0xdc,0xb2,0x30,0xf3}, {0xce,0xa9,0x27,0xee}, {0xc0,0xa0,0x2a,0xe5}, +{0x7a,0x47,0xb1,0x3c}, {0x74,0x4e,0xbc,0x37}, {0x66,0x55,0xab,0x2a}, {0x68,0x5c,0xa6,0x21}, +{0x42,0x63,0x85,0x10}, {0x4c,0x6a,0x88,0x1b}, {0x5e,0x71,0x9f,0x06}, {0x50,0x78,0x92,0x0d}, +{0x0a,0x0f,0xd9,0x64}, {0x04,0x06,0xd4,0x6f}, {0x16,0x1d,0xc3,0x72}, {0x18,0x14,0xce,0x79}, +{0x32,0x2b,0xed,0x48}, {0x3c,0x22,0xe0,0x43}, {0x2e,0x39,0xf7,0x5e}, {0x20,0x30,0xfa,0x55}, +{0xec,0x9a,0xb7,0x01}, {0xe2,0x93,0xba,0x0a}, {0xf0,0x88,0xad,0x17}, {0xfe,0x81,0xa0,0x1c}, +{0xd4,0xbe,0x83,0x2d}, {0xda,0xb7,0x8e,0x26}, {0xc8,0xac,0x99,0x3b}, {0xc6,0xa5,0x94,0x30}, +{0x9c,0xd2,0xdf,0x59}, {0x92,0xdb,0xd2,0x52}, {0x80,0xc0,0xc5,0x4f}, {0x8e,0xc9,0xc8,0x44}, +{0xa4,0xf6,0xeb,0x75}, {0xaa,0xff,0xe6,0x7e}, {0xb8,0xe4,0xf1,0x63}, {0xb6,0xed,0xfc,0x68}, +{0x0c,0x0a,0x67,0xb1}, {0x02,0x03,0x6a,0xba}, {0x10,0x18,0x7d,0xa7}, {0x1e,0x11,0x70,0xac}, +{0x34,0x2e,0x53,0x9d}, {0x3a,0x27,0x5e,0x96}, {0x28,0x3c,0x49,0x8b}, {0x26,0x35,0x44,0x80}, +{0x7c,0x42,0x0f,0xe9}, {0x72,0x4b,0x02,0xe2}, {0x60,0x50,0x15,0xff}, {0x6e,0x59,0x18,0xf4}, +{0x44,0x66,0x3b,0xc5}, {0x4a,0x6f,0x36,0xce}, {0x58,0x74,0x21,0xd3}, {0x56,0x7d,0x2c,0xd8}, +{0x37,0xa1,0x0c,0x7a}, {0x39,0xa8,0x01,0x71}, {0x2b,0xb3,0x16,0x6c}, {0x25,0xba,0x1b,0x67}, +{0x0f,0x85,0x38,0x56}, {0x01,0x8c,0x35,0x5d}, {0x13,0x97,0x22,0x40}, {0x1d,0x9e,0x2f,0x4b}, +{0x47,0xe9,0x64,0x22}, {0x49,0xe0,0x69,0x29}, {0x5b,0xfb,0x7e,0x34}, {0x55,0xf2,0x73,0x3f}, +{0x7f,0xcd,0x50,0x0e}, {0x71,0xc4,0x5d,0x05}, {0x63,0xdf,0x4a,0x18}, {0x6d,0xd6,0x47,0x13}, +{0xd7,0x31,0xdc,0xca}, {0xd9,0x38,0xd1,0xc1}, {0xcb,0x23,0xc6,0xdc}, {0xc5,0x2a,0xcb,0xd7}, +{0xef,0x15,0xe8,0xe6}, {0xe1,0x1c,0xe5,0xed}, {0xf3,0x07,0xf2,0xf0}, {0xfd,0x0e,0xff,0xfb}, +{0xa7,0x79,0xb4,0x92}, {0xa9,0x70,0xb9,0x99}, {0xbb,0x6b,0xae,0x84}, {0xb5,0x62,0xa3,0x8f}, +{0x9f,0x5d,0x80,0xbe}, {0x91,0x54,0x8d,0xb5}, {0x83,0x4f,0x9a,0xa8}, {0x8d,0x46,0x97,0xa3} +}; + +word8 U2[256][4] = { +{0x00,0x00,0x00,0x00}, {0x0b,0x0e,0x09,0x0d}, {0x16,0x1c,0x12,0x1a}, {0x1d,0x12,0x1b,0x17}, +{0x2c,0x38,0x24,0x34}, {0x27,0x36,0x2d,0x39}, {0x3a,0x24,0x36,0x2e}, {0x31,0x2a,0x3f,0x23}, +{0x58,0x70,0x48,0x68}, {0x53,0x7e,0x41,0x65}, {0x4e,0x6c,0x5a,0x72}, {0x45,0x62,0x53,0x7f}, +{0x74,0x48,0x6c,0x5c}, {0x7f,0x46,0x65,0x51}, {0x62,0x54,0x7e,0x46}, {0x69,0x5a,0x77,0x4b}, +{0xb0,0xe0,0x90,0xd0}, {0xbb,0xee,0x99,0xdd}, {0xa6,0xfc,0x82,0xca}, {0xad,0xf2,0x8b,0xc7}, +{0x9c,0xd8,0xb4,0xe4}, {0x97,0xd6,0xbd,0xe9}, {0x8a,0xc4,0xa6,0xfe}, {0x81,0xca,0xaf,0xf3}, +{0xe8,0x90,0xd8,0xb8}, {0xe3,0x9e,0xd1,0xb5}, {0xfe,0x8c,0xca,0xa2}, {0xf5,0x82,0xc3,0xaf}, +{0xc4,0xa8,0xfc,0x8c}, {0xcf,0xa6,0xf5,0x81}, {0xd2,0xb4,0xee,0x96}, {0xd9,0xba,0xe7,0x9b}, +{0x7b,0xdb,0x3b,0xbb}, {0x70,0xd5,0x32,0xb6}, {0x6d,0xc7,0x29,0xa1}, {0x66,0xc9,0x20,0xac}, +{0x57,0xe3,0x1f,0x8f}, {0x5c,0xed,0x16,0x82}, {0x41,0xff,0x0d,0x95}, {0x4a,0xf1,0x04,0x98}, +{0x23,0xab,0x73,0xd3}, {0x28,0xa5,0x7a,0xde}, {0x35,0xb7,0x61,0xc9}, {0x3e,0xb9,0x68,0xc4}, +{0x0f,0x93,0x57,0xe7}, {0x04,0x9d,0x5e,0xea}, {0x19,0x8f,0x45,0xfd}, {0x12,0x81,0x4c,0xf0}, +{0xcb,0x3b,0xab,0x6b}, {0xc0,0x35,0xa2,0x66}, {0xdd,0x27,0xb9,0x71}, {0xd6,0x29,0xb0,0x7c}, +{0xe7,0x03,0x8f,0x5f}, {0xec,0x0d,0x86,0x52}, {0xf1,0x1f,0x9d,0x45}, {0xfa,0x11,0x94,0x48}, +{0x93,0x4b,0xe3,0x03}, {0x98,0x45,0xea,0x0e}, {0x85,0x57,0xf1,0x19}, {0x8e,0x59,0xf8,0x14}, +{0xbf,0x73,0xc7,0x37}, {0xb4,0x7d,0xce,0x3a}, {0xa9,0x6f,0xd5,0x2d}, {0xa2,0x61,0xdc,0x20}, +{0xf6,0xad,0x76,0x6d}, {0xfd,0xa3,0x7f,0x60}, {0xe0,0xb1,0x64,0x77}, {0xeb,0xbf,0x6d,0x7a}, +{0xda,0x95,0x52,0x59}, {0xd1,0x9b,0x5b,0x54}, {0xcc,0x89,0x40,0x43}, {0xc7,0x87,0x49,0x4e}, +{0xae,0xdd,0x3e,0x05}, {0xa5,0xd3,0x37,0x08}, {0xb8,0xc1,0x2c,0x1f}, {0xb3,0xcf,0x25,0x12}, +{0x82,0xe5,0x1a,0x31}, {0x89,0xeb,0x13,0x3c}, {0x94,0xf9,0x08,0x2b}, {0x9f,0xf7,0x01,0x26}, +{0x46,0x4d,0xe6,0xbd}, {0x4d,0x43,0xef,0xb0}, {0x50,0x51,0xf4,0xa7}, {0x5b,0x5f,0xfd,0xaa}, +{0x6a,0x75,0xc2,0x89}, {0x61,0x7b,0xcb,0x84}, {0x7c,0x69,0xd0,0x93}, {0x77,0x67,0xd9,0x9e}, +{0x1e,0x3d,0xae,0xd5}, {0x15,0x33,0xa7,0xd8}, {0x08,0x21,0xbc,0xcf}, {0x03,0x2f,0xb5,0xc2}, +{0x32,0x05,0x8a,0xe1}, {0x39,0x0b,0x83,0xec}, {0x24,0x19,0x98,0xfb}, {0x2f,0x17,0x91,0xf6}, +{0x8d,0x76,0x4d,0xd6}, {0x86,0x78,0x44,0xdb}, {0x9b,0x6a,0x5f,0xcc}, {0x90,0x64,0x56,0xc1}, +{0xa1,0x4e,0x69,0xe2}, {0xaa,0x40,0x60,0xef}, {0xb7,0x52,0x7b,0xf8}, {0xbc,0x5c,0x72,0xf5}, +{0xd5,0x06,0x05,0xbe}, {0xde,0x08,0x0c,0xb3}, {0xc3,0x1a,0x17,0xa4}, {0xc8,0x14,0x1e,0xa9}, +{0xf9,0x3e,0x21,0x8a}, {0xf2,0x30,0x28,0x87}, {0xef,0x22,0x33,0x90}, {0xe4,0x2c,0x3a,0x9d}, +{0x3d,0x96,0xdd,0x06}, {0x36,0x98,0xd4,0x0b}, {0x2b,0x8a,0xcf,0x1c}, {0x20,0x84,0xc6,0x11}, +{0x11,0xae,0xf9,0x32}, {0x1a,0xa0,0xf0,0x3f}, {0x07,0xb2,0xeb,0x28}, {0x0c,0xbc,0xe2,0x25}, +{0x65,0xe6,0x95,0x6e}, {0x6e,0xe8,0x9c,0x63}, {0x73,0xfa,0x87,0x74}, {0x78,0xf4,0x8e,0x79}, +{0x49,0xde,0xb1,0x5a}, {0x42,0xd0,0xb8,0x57}, {0x5f,0xc2,0xa3,0x40}, {0x54,0xcc,0xaa,0x4d}, +{0xf7,0x41,0xec,0xda}, {0xfc,0x4f,0xe5,0xd7}, {0xe1,0x5d,0xfe,0xc0}, {0xea,0x53,0xf7,0xcd}, +{0xdb,0x79,0xc8,0xee}, {0xd0,0x77,0xc1,0xe3}, {0xcd,0x65,0xda,0xf4}, {0xc6,0x6b,0xd3,0xf9}, +{0xaf,0x31,0xa4,0xb2}, {0xa4,0x3f,0xad,0xbf}, {0xb9,0x2d,0xb6,0xa8}, {0xb2,0x23,0xbf,0xa5}, +{0x83,0x09,0x80,0x86}, {0x88,0x07,0x89,0x8b}, {0x95,0x15,0x92,0x9c}, {0x9e,0x1b,0x9b,0x91}, +{0x47,0xa1,0x7c,0x0a}, {0x4c,0xaf,0x75,0x07}, {0x51,0xbd,0x6e,0x10}, {0x5a,0xb3,0x67,0x1d}, +{0x6b,0x99,0x58,0x3e}, {0x60,0x97,0x51,0x33}, {0x7d,0x85,0x4a,0x24}, {0x76,0x8b,0x43,0x29}, +{0x1f,0xd1,0x34,0x62}, {0x14,0xdf,0x3d,0x6f}, {0x09,0xcd,0x26,0x78}, {0x02,0xc3,0x2f,0x75}, +{0x33,0xe9,0x10,0x56}, {0x38,0xe7,0x19,0x5b}, {0x25,0xf5,0x02,0x4c}, {0x2e,0xfb,0x0b,0x41}, +{0x8c,0x9a,0xd7,0x61}, {0x87,0x94,0xde,0x6c}, {0x9a,0x86,0xc5,0x7b}, {0x91,0x88,0xcc,0x76}, +{0xa0,0xa2,0xf3,0x55}, {0xab,0xac,0xfa,0x58}, {0xb6,0xbe,0xe1,0x4f}, {0xbd,0xb0,0xe8,0x42}, +{0xd4,0xea,0x9f,0x09}, {0xdf,0xe4,0x96,0x04}, {0xc2,0xf6,0x8d,0x13}, {0xc9,0xf8,0x84,0x1e}, +{0xf8,0xd2,0xbb,0x3d}, {0xf3,0xdc,0xb2,0x30}, {0xee,0xce,0xa9,0x27}, {0xe5,0xc0,0xa0,0x2a}, +{0x3c,0x7a,0x47,0xb1}, {0x37,0x74,0x4e,0xbc}, {0x2a,0x66,0x55,0xab}, {0x21,0x68,0x5c,0xa6}, +{0x10,0x42,0x63,0x85}, {0x1b,0x4c,0x6a,0x88}, {0x06,0x5e,0x71,0x9f}, {0x0d,0x50,0x78,0x92}, +{0x64,0x0a,0x0f,0xd9}, {0x6f,0x04,0x06,0xd4}, {0x72,0x16,0x1d,0xc3}, {0x79,0x18,0x14,0xce}, +{0x48,0x32,0x2b,0xed}, {0x43,0x3c,0x22,0xe0}, {0x5e,0x2e,0x39,0xf7}, {0x55,0x20,0x30,0xfa}, +{0x01,0xec,0x9a,0xb7}, {0x0a,0xe2,0x93,0xba}, {0x17,0xf0,0x88,0xad}, {0x1c,0xfe,0x81,0xa0}, +{0x2d,0xd4,0xbe,0x83}, {0x26,0xda,0xb7,0x8e}, {0x3b,0xc8,0xac,0x99}, {0x30,0xc6,0xa5,0x94}, +{0x59,0x9c,0xd2,0xdf}, {0x52,0x92,0xdb,0xd2}, {0x4f,0x80,0xc0,0xc5}, {0x44,0x8e,0xc9,0xc8}, +{0x75,0xa4,0xf6,0xeb}, {0x7e,0xaa,0xff,0xe6}, {0x63,0xb8,0xe4,0xf1}, {0x68,0xb6,0xed,0xfc}, +{0xb1,0x0c,0x0a,0x67}, {0xba,0x02,0x03,0x6a}, {0xa7,0x10,0x18,0x7d}, {0xac,0x1e,0x11,0x70}, +{0x9d,0x34,0x2e,0x53}, {0x96,0x3a,0x27,0x5e}, {0x8b,0x28,0x3c,0x49}, {0x80,0x26,0x35,0x44}, +{0xe9,0x7c,0x42,0x0f}, {0xe2,0x72,0x4b,0x02}, {0xff,0x60,0x50,0x15}, {0xf4,0x6e,0x59,0x18}, +{0xc5,0x44,0x66,0x3b}, {0xce,0x4a,0x6f,0x36}, {0xd3,0x58,0x74,0x21}, {0xd8,0x56,0x7d,0x2c}, +{0x7a,0x37,0xa1,0x0c}, {0x71,0x39,0xa8,0x01}, {0x6c,0x2b,0xb3,0x16}, {0x67,0x25,0xba,0x1b}, +{0x56,0x0f,0x85,0x38}, {0x5d,0x01,0x8c,0x35}, {0x40,0x13,0x97,0x22}, {0x4b,0x1d,0x9e,0x2f}, +{0x22,0x47,0xe9,0x64}, {0x29,0x49,0xe0,0x69}, {0x34,0x5b,0xfb,0x7e}, {0x3f,0x55,0xf2,0x73}, +{0x0e,0x7f,0xcd,0x50}, {0x05,0x71,0xc4,0x5d}, {0x18,0x63,0xdf,0x4a}, {0x13,0x6d,0xd6,0x47}, +{0xca,0xd7,0x31,0xdc}, {0xc1,0xd9,0x38,0xd1}, {0xdc,0xcb,0x23,0xc6}, {0xd7,0xc5,0x2a,0xcb}, +{0xe6,0xef,0x15,0xe8}, {0xed,0xe1,0x1c,0xe5}, {0xf0,0xf3,0x07,0xf2}, {0xfb,0xfd,0x0e,0xff}, +{0x92,0xa7,0x79,0xb4}, {0x99,0xa9,0x70,0xb9}, {0x84,0xbb,0x6b,0xae}, {0x8f,0xb5,0x62,0xa3}, +{0xbe,0x9f,0x5d,0x80}, {0xb5,0x91,0x54,0x8d}, {0xa8,0x83,0x4f,0x9a}, {0xa3,0x8d,0x46,0x97} +}; + +word8 U3[256][4] = { +{0x00,0x00,0x00,0x00}, {0x0d,0x0b,0x0e,0x09}, {0x1a,0x16,0x1c,0x12}, {0x17,0x1d,0x12,0x1b}, +{0x34,0x2c,0x38,0x24}, {0x39,0x27,0x36,0x2d}, {0x2e,0x3a,0x24,0x36}, {0x23,0x31,0x2a,0x3f}, +{0x68,0x58,0x70,0x48}, {0x65,0x53,0x7e,0x41}, {0x72,0x4e,0x6c,0x5a}, {0x7f,0x45,0x62,0x53}, +{0x5c,0x74,0x48,0x6c}, {0x51,0x7f,0x46,0x65}, {0x46,0x62,0x54,0x7e}, {0x4b,0x69,0x5a,0x77}, +{0xd0,0xb0,0xe0,0x90}, {0xdd,0xbb,0xee,0x99}, {0xca,0xa6,0xfc,0x82}, {0xc7,0xad,0xf2,0x8b}, +{0xe4,0x9c,0xd8,0xb4}, {0xe9,0x97,0xd6,0xbd}, {0xfe,0x8a,0xc4,0xa6}, {0xf3,0x81,0xca,0xaf}, +{0xb8,0xe8,0x90,0xd8}, {0xb5,0xe3,0x9e,0xd1}, {0xa2,0xfe,0x8c,0xca}, {0xaf,0xf5,0x82,0xc3}, +{0x8c,0xc4,0xa8,0xfc}, {0x81,0xcf,0xa6,0xf5}, {0x96,0xd2,0xb4,0xee}, {0x9b,0xd9,0xba,0xe7}, +{0xbb,0x7b,0xdb,0x3b}, {0xb6,0x70,0xd5,0x32}, {0xa1,0x6d,0xc7,0x29}, {0xac,0x66,0xc9,0x20}, +{0x8f,0x57,0xe3,0x1f}, {0x82,0x5c,0xed,0x16}, {0x95,0x41,0xff,0x0d}, {0x98,0x4a,0xf1,0x04}, +{0xd3,0x23,0xab,0x73}, {0xde,0x28,0xa5,0x7a}, {0xc9,0x35,0xb7,0x61}, {0xc4,0x3e,0xb9,0x68}, +{0xe7,0x0f,0x93,0x57}, {0xea,0x04,0x9d,0x5e}, {0xfd,0x19,0x8f,0x45}, {0xf0,0x12,0x81,0x4c}, +{0x6b,0xcb,0x3b,0xab}, {0x66,0xc0,0x35,0xa2}, {0x71,0xdd,0x27,0xb9}, {0x7c,0xd6,0x29,0xb0}, +{0x5f,0xe7,0x03,0x8f}, {0x52,0xec,0x0d,0x86}, {0x45,0xf1,0x1f,0x9d}, {0x48,0xfa,0x11,0x94}, +{0x03,0x93,0x4b,0xe3}, {0x0e,0x98,0x45,0xea}, {0x19,0x85,0x57,0xf1}, {0x14,0x8e,0x59,0xf8}, +{0x37,0xbf,0x73,0xc7}, {0x3a,0xb4,0x7d,0xce}, {0x2d,0xa9,0x6f,0xd5}, {0x20,0xa2,0x61,0xdc}, +{0x6d,0xf6,0xad,0x76}, {0x60,0xfd,0xa3,0x7f}, {0x77,0xe0,0xb1,0x64}, {0x7a,0xeb,0xbf,0x6d}, +{0x59,0xda,0x95,0x52}, {0x54,0xd1,0x9b,0x5b}, {0x43,0xcc,0x89,0x40}, {0x4e,0xc7,0x87,0x49}, +{0x05,0xae,0xdd,0x3e}, {0x08,0xa5,0xd3,0x37}, {0x1f,0xb8,0xc1,0x2c}, {0x12,0xb3,0xcf,0x25}, +{0x31,0x82,0xe5,0x1a}, {0x3c,0x89,0xeb,0x13}, {0x2b,0x94,0xf9,0x08}, {0x26,0x9f,0xf7,0x01}, +{0xbd,0x46,0x4d,0xe6}, {0xb0,0x4d,0x43,0xef}, {0xa7,0x50,0x51,0xf4}, {0xaa,0x5b,0x5f,0xfd}, +{0x89,0x6a,0x75,0xc2}, {0x84,0x61,0x7b,0xcb}, {0x93,0x7c,0x69,0xd0}, {0x9e,0x77,0x67,0xd9}, +{0xd5,0x1e,0x3d,0xae}, {0xd8,0x15,0x33,0xa7}, {0xcf,0x08,0x21,0xbc}, {0xc2,0x03,0x2f,0xb5}, +{0xe1,0x32,0x05,0x8a}, {0xec,0x39,0x0b,0x83}, {0xfb,0x24,0x19,0x98}, {0xf6,0x2f,0x17,0x91}, +{0xd6,0x8d,0x76,0x4d}, {0xdb,0x86,0x78,0x44}, {0xcc,0x9b,0x6a,0x5f}, {0xc1,0x90,0x64,0x56}, +{0xe2,0xa1,0x4e,0x69}, {0xef,0xaa,0x40,0x60}, {0xf8,0xb7,0x52,0x7b}, {0xf5,0xbc,0x5c,0x72}, +{0xbe,0xd5,0x06,0x05}, {0xb3,0xde,0x08,0x0c}, {0xa4,0xc3,0x1a,0x17}, {0xa9,0xc8,0x14,0x1e}, +{0x8a,0xf9,0x3e,0x21}, {0x87,0xf2,0x30,0x28}, {0x90,0xef,0x22,0x33}, {0x9d,0xe4,0x2c,0x3a}, +{0x06,0x3d,0x96,0xdd}, {0x0b,0x36,0x98,0xd4}, {0x1c,0x2b,0x8a,0xcf}, {0x11,0x20,0x84,0xc6}, +{0x32,0x11,0xae,0xf9}, {0x3f,0x1a,0xa0,0xf0}, {0x28,0x07,0xb2,0xeb}, {0x25,0x0c,0xbc,0xe2}, +{0x6e,0x65,0xe6,0x95}, {0x63,0x6e,0xe8,0x9c}, {0x74,0x73,0xfa,0x87}, {0x79,0x78,0xf4,0x8e}, +{0x5a,0x49,0xde,0xb1}, {0x57,0x42,0xd0,0xb8}, {0x40,0x5f,0xc2,0xa3}, {0x4d,0x54,0xcc,0xaa}, +{0xda,0xf7,0x41,0xec}, {0xd7,0xfc,0x4f,0xe5}, {0xc0,0xe1,0x5d,0xfe}, {0xcd,0xea,0x53,0xf7}, +{0xee,0xdb,0x79,0xc8}, {0xe3,0xd0,0x77,0xc1}, {0xf4,0xcd,0x65,0xda}, {0xf9,0xc6,0x6b,0xd3}, +{0xb2,0xaf,0x31,0xa4}, {0xbf,0xa4,0x3f,0xad}, {0xa8,0xb9,0x2d,0xb6}, {0xa5,0xb2,0x23,0xbf}, +{0x86,0x83,0x09,0x80}, {0x8b,0x88,0x07,0x89}, {0x9c,0x95,0x15,0x92}, {0x91,0x9e,0x1b,0x9b}, +{0x0a,0x47,0xa1,0x7c}, {0x07,0x4c,0xaf,0x75}, {0x10,0x51,0xbd,0x6e}, {0x1d,0x5a,0xb3,0x67}, +{0x3e,0x6b,0x99,0x58}, {0x33,0x60,0x97,0x51}, {0x24,0x7d,0x85,0x4a}, {0x29,0x76,0x8b,0x43}, +{0x62,0x1f,0xd1,0x34}, {0x6f,0x14,0xdf,0x3d}, {0x78,0x09,0xcd,0x26}, {0x75,0x02,0xc3,0x2f}, +{0x56,0x33,0xe9,0x10}, {0x5b,0x38,0xe7,0x19}, {0x4c,0x25,0xf5,0x02}, {0x41,0x2e,0xfb,0x0b}, +{0x61,0x8c,0x9a,0xd7}, {0x6c,0x87,0x94,0xde}, {0x7b,0x9a,0x86,0xc5}, {0x76,0x91,0x88,0xcc}, +{0x55,0xa0,0xa2,0xf3}, {0x58,0xab,0xac,0xfa}, {0x4f,0xb6,0xbe,0xe1}, {0x42,0xbd,0xb0,0xe8}, +{0x09,0xd4,0xea,0x9f}, {0x04,0xdf,0xe4,0x96}, {0x13,0xc2,0xf6,0x8d}, {0x1e,0xc9,0xf8,0x84}, +{0x3d,0xf8,0xd2,0xbb}, {0x30,0xf3,0xdc,0xb2}, {0x27,0xee,0xce,0xa9}, {0x2a,0xe5,0xc0,0xa0}, +{0xb1,0x3c,0x7a,0x47}, {0xbc,0x37,0x74,0x4e}, {0xab,0x2a,0x66,0x55}, {0xa6,0x21,0x68,0x5c}, +{0x85,0x10,0x42,0x63}, {0x88,0x1b,0x4c,0x6a}, {0x9f,0x06,0x5e,0x71}, {0x92,0x0d,0x50,0x78}, +{0xd9,0x64,0x0a,0x0f}, {0xd4,0x6f,0x04,0x06}, {0xc3,0x72,0x16,0x1d}, {0xce,0x79,0x18,0x14}, +{0xed,0x48,0x32,0x2b}, {0xe0,0x43,0x3c,0x22}, {0xf7,0x5e,0x2e,0x39}, {0xfa,0x55,0x20,0x30}, +{0xb7,0x01,0xec,0x9a}, {0xba,0x0a,0xe2,0x93}, {0xad,0x17,0xf0,0x88}, {0xa0,0x1c,0xfe,0x81}, +{0x83,0x2d,0xd4,0xbe}, {0x8e,0x26,0xda,0xb7}, {0x99,0x3b,0xc8,0xac}, {0x94,0x30,0xc6,0xa5}, +{0xdf,0x59,0x9c,0xd2}, {0xd2,0x52,0x92,0xdb}, {0xc5,0x4f,0x80,0xc0}, {0xc8,0x44,0x8e,0xc9}, +{0xeb,0x75,0xa4,0xf6}, {0xe6,0x7e,0xaa,0xff}, {0xf1,0x63,0xb8,0xe4}, {0xfc,0x68,0xb6,0xed}, +{0x67,0xb1,0x0c,0x0a}, {0x6a,0xba,0x02,0x03}, {0x7d,0xa7,0x10,0x18}, {0x70,0xac,0x1e,0x11}, +{0x53,0x9d,0x34,0x2e}, {0x5e,0x96,0x3a,0x27}, {0x49,0x8b,0x28,0x3c}, {0x44,0x80,0x26,0x35}, +{0x0f,0xe9,0x7c,0x42}, {0x02,0xe2,0x72,0x4b}, {0x15,0xff,0x60,0x50}, {0x18,0xf4,0x6e,0x59}, +{0x3b,0xc5,0x44,0x66}, {0x36,0xce,0x4a,0x6f}, {0x21,0xd3,0x58,0x74}, {0x2c,0xd8,0x56,0x7d}, +{0x0c,0x7a,0x37,0xa1}, {0x01,0x71,0x39,0xa8}, {0x16,0x6c,0x2b,0xb3}, {0x1b,0x67,0x25,0xba}, +{0x38,0x56,0x0f,0x85}, {0x35,0x5d,0x01,0x8c}, {0x22,0x40,0x13,0x97}, {0x2f,0x4b,0x1d,0x9e}, +{0x64,0x22,0x47,0xe9}, {0x69,0x29,0x49,0xe0}, {0x7e,0x34,0x5b,0xfb}, {0x73,0x3f,0x55,0xf2}, +{0x50,0x0e,0x7f,0xcd}, {0x5d,0x05,0x71,0xc4}, {0x4a,0x18,0x63,0xdf}, {0x47,0x13,0x6d,0xd6}, +{0xdc,0xca,0xd7,0x31}, {0xd1,0xc1,0xd9,0x38}, {0xc6,0xdc,0xcb,0x23}, {0xcb,0xd7,0xc5,0x2a}, +{0xe8,0xe6,0xef,0x15}, {0xe5,0xed,0xe1,0x1c}, {0xf2,0xf0,0xf3,0x07}, {0xff,0xfb,0xfd,0x0e}, +{0xb4,0x92,0xa7,0x79}, {0xb9,0x99,0xa9,0x70}, {0xae,0x84,0xbb,0x6b}, {0xa3,0x8f,0xb5,0x62}, +{0x80,0xbe,0x9f,0x5d}, {0x8d,0xb5,0x91,0x54}, {0x9a,0xa8,0x83,0x4f}, {0x97,0xa3,0x8d,0x46} +}; + +word8 U4[256][4] = { +{0x00,0x00,0x00,0x00}, {0x09,0x0d,0x0b,0x0e}, {0x12,0x1a,0x16,0x1c}, {0x1b,0x17,0x1d,0x12}, +{0x24,0x34,0x2c,0x38}, {0x2d,0x39,0x27,0x36}, {0x36,0x2e,0x3a,0x24}, {0x3f,0x23,0x31,0x2a}, +{0x48,0x68,0x58,0x70}, {0x41,0x65,0x53,0x7e}, {0x5a,0x72,0x4e,0x6c}, {0x53,0x7f,0x45,0x62}, +{0x6c,0x5c,0x74,0x48}, {0x65,0x51,0x7f,0x46}, {0x7e,0x46,0x62,0x54}, {0x77,0x4b,0x69,0x5a}, +{0x90,0xd0,0xb0,0xe0}, {0x99,0xdd,0xbb,0xee}, {0x82,0xca,0xa6,0xfc}, {0x8b,0xc7,0xad,0xf2}, +{0xb4,0xe4,0x9c,0xd8}, {0xbd,0xe9,0x97,0xd6}, {0xa6,0xfe,0x8a,0xc4}, {0xaf,0xf3,0x81,0xca}, +{0xd8,0xb8,0xe8,0x90}, {0xd1,0xb5,0xe3,0x9e}, {0xca,0xa2,0xfe,0x8c}, {0xc3,0xaf,0xf5,0x82}, +{0xfc,0x8c,0xc4,0xa8}, {0xf5,0x81,0xcf,0xa6}, {0xee,0x96,0xd2,0xb4}, {0xe7,0x9b,0xd9,0xba}, +{0x3b,0xbb,0x7b,0xdb}, {0x32,0xb6,0x70,0xd5}, {0x29,0xa1,0x6d,0xc7}, {0x20,0xac,0x66,0xc9}, +{0x1f,0x8f,0x57,0xe3}, {0x16,0x82,0x5c,0xed}, {0x0d,0x95,0x41,0xff}, {0x04,0x98,0x4a,0xf1}, +{0x73,0xd3,0x23,0xab}, {0x7a,0xde,0x28,0xa5}, {0x61,0xc9,0x35,0xb7}, {0x68,0xc4,0x3e,0xb9}, +{0x57,0xe7,0x0f,0x93}, {0x5e,0xea,0x04,0x9d}, {0x45,0xfd,0x19,0x8f}, {0x4c,0xf0,0x12,0x81}, +{0xab,0x6b,0xcb,0x3b}, {0xa2,0x66,0xc0,0x35}, {0xb9,0x71,0xdd,0x27}, {0xb0,0x7c,0xd6,0x29}, +{0x8f,0x5f,0xe7,0x03}, {0x86,0x52,0xec,0x0d}, {0x9d,0x45,0xf1,0x1f}, {0x94,0x48,0xfa,0x11}, +{0xe3,0x03,0x93,0x4b}, {0xea,0x0e,0x98,0x45}, {0xf1,0x19,0x85,0x57}, {0xf8,0x14,0x8e,0x59}, +{0xc7,0x37,0xbf,0x73}, {0xce,0x3a,0xb4,0x7d}, {0xd5,0x2d,0xa9,0x6f}, {0xdc,0x20,0xa2,0x61}, +{0x76,0x6d,0xf6,0xad}, {0x7f,0x60,0xfd,0xa3}, {0x64,0x77,0xe0,0xb1}, {0x6d,0x7a,0xeb,0xbf}, +{0x52,0x59,0xda,0x95}, {0x5b,0x54,0xd1,0x9b}, {0x40,0x43,0xcc,0x89}, {0x49,0x4e,0xc7,0x87}, +{0x3e,0x05,0xae,0xdd}, {0x37,0x08,0xa5,0xd3}, {0x2c,0x1f,0xb8,0xc1}, {0x25,0x12,0xb3,0xcf}, +{0x1a,0x31,0x82,0xe5}, {0x13,0x3c,0x89,0xeb}, {0x08,0x2b,0x94,0xf9}, {0x01,0x26,0x9f,0xf7}, +{0xe6,0xbd,0x46,0x4d}, {0xef,0xb0,0x4d,0x43}, {0xf4,0xa7,0x50,0x51}, {0xfd,0xaa,0x5b,0x5f}, +{0xc2,0x89,0x6a,0x75}, {0xcb,0x84,0x61,0x7b}, {0xd0,0x93,0x7c,0x69}, {0xd9,0x9e,0x77,0x67}, +{0xae,0xd5,0x1e,0x3d}, {0xa7,0xd8,0x15,0x33}, {0xbc,0xcf,0x08,0x21}, {0xb5,0xc2,0x03,0x2f}, +{0x8a,0xe1,0x32,0x05}, {0x83,0xec,0x39,0x0b}, {0x98,0xfb,0x24,0x19}, {0x91,0xf6,0x2f,0x17}, +{0x4d,0xd6,0x8d,0x76}, {0x44,0xdb,0x86,0x78}, {0x5f,0xcc,0x9b,0x6a}, {0x56,0xc1,0x90,0x64}, +{0x69,0xe2,0xa1,0x4e}, {0x60,0xef,0xaa,0x40}, {0x7b,0xf8,0xb7,0x52}, {0x72,0xf5,0xbc,0x5c}, +{0x05,0xbe,0xd5,0x06}, {0x0c,0xb3,0xde,0x08}, {0x17,0xa4,0xc3,0x1a}, {0x1e,0xa9,0xc8,0x14}, +{0x21,0x8a,0xf9,0x3e}, {0x28,0x87,0xf2,0x30}, {0x33,0x90,0xef,0x22}, {0x3a,0x9d,0xe4,0x2c}, +{0xdd,0x06,0x3d,0x96}, {0xd4,0x0b,0x36,0x98}, {0xcf,0x1c,0x2b,0x8a}, {0xc6,0x11,0x20,0x84}, +{0xf9,0x32,0x11,0xae}, {0xf0,0x3f,0x1a,0xa0}, {0xeb,0x28,0x07,0xb2}, {0xe2,0x25,0x0c,0xbc}, +{0x95,0x6e,0x65,0xe6}, {0x9c,0x63,0x6e,0xe8}, {0x87,0x74,0x73,0xfa}, {0x8e,0x79,0x78,0xf4}, +{0xb1,0x5a,0x49,0xde}, {0xb8,0x57,0x42,0xd0}, {0xa3,0x40,0x5f,0xc2}, {0xaa,0x4d,0x54,0xcc}, +{0xec,0xda,0xf7,0x41}, {0xe5,0xd7,0xfc,0x4f}, {0xfe,0xc0,0xe1,0x5d}, {0xf7,0xcd,0xea,0x53}, +{0xc8,0xee,0xdb,0x79}, {0xc1,0xe3,0xd0,0x77}, {0xda,0xf4,0xcd,0x65}, {0xd3,0xf9,0xc6,0x6b}, +{0xa4,0xb2,0xaf,0x31}, {0xad,0xbf,0xa4,0x3f}, {0xb6,0xa8,0xb9,0x2d}, {0xbf,0xa5,0xb2,0x23}, +{0x80,0x86,0x83,0x09}, {0x89,0x8b,0x88,0x07}, {0x92,0x9c,0x95,0x15}, {0x9b,0x91,0x9e,0x1b}, +{0x7c,0x0a,0x47,0xa1}, {0x75,0x07,0x4c,0xaf}, {0x6e,0x10,0x51,0xbd}, {0x67,0x1d,0x5a,0xb3}, +{0x58,0x3e,0x6b,0x99}, {0x51,0x33,0x60,0x97}, {0x4a,0x24,0x7d,0x85}, {0x43,0x29,0x76,0x8b}, +{0x34,0x62,0x1f,0xd1}, {0x3d,0x6f,0x14,0xdf}, {0x26,0x78,0x09,0xcd}, {0x2f,0x75,0x02,0xc3}, +{0x10,0x56,0x33,0xe9}, {0x19,0x5b,0x38,0xe7}, {0x02,0x4c,0x25,0xf5}, {0x0b,0x41,0x2e,0xfb}, +{0xd7,0x61,0x8c,0x9a}, {0xde,0x6c,0x87,0x94}, {0xc5,0x7b,0x9a,0x86}, {0xcc,0x76,0x91,0x88}, +{0xf3,0x55,0xa0,0xa2}, {0xfa,0x58,0xab,0xac}, {0xe1,0x4f,0xb6,0xbe}, {0xe8,0x42,0xbd,0xb0}, +{0x9f,0x09,0xd4,0xea}, {0x96,0x04,0xdf,0xe4}, {0x8d,0x13,0xc2,0xf6}, {0x84,0x1e,0xc9,0xf8}, +{0xbb,0x3d,0xf8,0xd2}, {0xb2,0x30,0xf3,0xdc}, {0xa9,0x27,0xee,0xce}, {0xa0,0x2a,0xe5,0xc0}, +{0x47,0xb1,0x3c,0x7a}, {0x4e,0xbc,0x37,0x74}, {0x55,0xab,0x2a,0x66}, {0x5c,0xa6,0x21,0x68}, +{0x63,0x85,0x10,0x42}, {0x6a,0x88,0x1b,0x4c}, {0x71,0x9f,0x06,0x5e}, {0x78,0x92,0x0d,0x50}, +{0x0f,0xd9,0x64,0x0a}, {0x06,0xd4,0x6f,0x04}, {0x1d,0xc3,0x72,0x16}, {0x14,0xce,0x79,0x18}, +{0x2b,0xed,0x48,0x32}, {0x22,0xe0,0x43,0x3c}, {0x39,0xf7,0x5e,0x2e}, {0x30,0xfa,0x55,0x20}, +{0x9a,0xb7,0x01,0xec}, {0x93,0xba,0x0a,0xe2}, {0x88,0xad,0x17,0xf0}, {0x81,0xa0,0x1c,0xfe}, +{0xbe,0x83,0x2d,0xd4}, {0xb7,0x8e,0x26,0xda}, {0xac,0x99,0x3b,0xc8}, {0xa5,0x94,0x30,0xc6}, +{0xd2,0xdf,0x59,0x9c}, {0xdb,0xd2,0x52,0x92}, {0xc0,0xc5,0x4f,0x80}, {0xc9,0xc8,0x44,0x8e}, +{0xf6,0xeb,0x75,0xa4}, {0xff,0xe6,0x7e,0xaa}, {0xe4,0xf1,0x63,0xb8}, {0xed,0xfc,0x68,0xb6}, +{0x0a,0x67,0xb1,0x0c}, {0x03,0x6a,0xba,0x02}, {0x18,0x7d,0xa7,0x10}, {0x11,0x70,0xac,0x1e}, +{0x2e,0x53,0x9d,0x34}, {0x27,0x5e,0x96,0x3a}, {0x3c,0x49,0x8b,0x28}, {0x35,0x44,0x80,0x26}, +{0x42,0x0f,0xe9,0x7c}, {0x4b,0x02,0xe2,0x72}, {0x50,0x15,0xff,0x60}, {0x59,0x18,0xf4,0x6e}, +{0x66,0x3b,0xc5,0x44}, {0x6f,0x36,0xce,0x4a}, {0x74,0x21,0xd3,0x58}, {0x7d,0x2c,0xd8,0x56}, +{0xa1,0x0c,0x7a,0x37}, {0xa8,0x01,0x71,0x39}, {0xb3,0x16,0x6c,0x2b}, {0xba,0x1b,0x67,0x25}, +{0x85,0x38,0x56,0x0f}, {0x8c,0x35,0x5d,0x01}, {0x97,0x22,0x40,0x13}, {0x9e,0x2f,0x4b,0x1d}, +{0xe9,0x64,0x22,0x47}, {0xe0,0x69,0x29,0x49}, {0xfb,0x7e,0x34,0x5b}, {0xf2,0x73,0x3f,0x55}, +{0xcd,0x50,0x0e,0x7f}, {0xc4,0x5d,0x05,0x71}, {0xdf,0x4a,0x18,0x63}, {0xd6,0x47,0x13,0x6d}, +{0x31,0xdc,0xca,0xd7}, {0x38,0xd1,0xc1,0xd9}, {0x23,0xc6,0xdc,0xcb}, {0x2a,0xcb,0xd7,0xc5}, +{0x15,0xe8,0xe6,0xef}, {0x1c,0xe5,0xed,0xe1}, {0x07,0xf2,0xf0,0xf3}, {0x0e,0xff,0xfb,0xfd}, +{0x79,0xb4,0x92,0xa7}, {0x70,0xb9,0x99,0xa9}, {0x6b,0xae,0x84,0xbb}, {0x62,0xa3,0x8f,0xb5}, +{0x5d,0x80,0xbe,0x9f}, {0x54,0x8d,0xb5,0x91}, {0x4f,0x9a,0xa8,0x83}, {0x46,0x97,0xa3,0x8d} +}; + +word32 rcon[30] = { + 0x01,0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 }; diff --git a/Multiplayer/raknet/Rijndael.h b/Multiplayer/raknet/Rijndael.h new file mode 100644 index 000000000..3b15878b1 --- /dev/null +++ b/Multiplayer/raknet/Rijndael.h @@ -0,0 +1,125 @@ +/// \file +/// \brief \b [Internal] AES encoding / decoding +/// rijndael-alg-fst.h v2.0 August '99 +/// Optimised ANSI C code +/// taken from the 'aescrypt' project: www.sf.net/projects/aescrypt +/// See LICENSE-EST for the license applicable to this file + + +/// \note Although the routines claim to support 192 and 256 bit blocks, +/// don't take your chances - stick to the 128 bit (16 byte) blocks unless +/// you've run tests to prove that 192 and 256 are correctly supported. +/// - Cirilo + + +#include + +#ifndef __RIJNDAEL_ALG_H +#define __RIJNDAEL_ALG_H + +#define MAXKC (256/32) +#define MAXROUNDS 14 + +typedef unsigned char word8; +typedef unsigned short word16; +typedef unsigned int word32; + +int rijndaelKeySched (word8 k[MAXKC][4], int keyBits, + word8 rk[MAXROUNDS+1][4][4]); +int rijndaelKeyEnctoDec (int keyBits, word8 W[MAXROUNDS+1][4][4]); +int rijndaelEncrypt (word8 a[16], word8 b[16], + word8 rk[MAXROUNDS+1][4][4]); +int rijndaelEncryptRound (word8 a[4][4], + word8 rk[MAXROUNDS+1][4][4], int rounds); +int rijndaelDecrypt (word8 a[16], word8 b[16], + word8 rk[MAXROUNDS+1][4][4]); +int rijndaelDecryptRound (word8 a[4][4], + word8 rk[MAXROUNDS+1][4][4], int rounds); + +#endif + +// End of algorithm headers. begin the AES API header defs + + +#ifndef __RIJNDAEL_API_H +#define __RIJNDAEL_API_H + +// rijndael-api-fst.h v2.0 August '99 +// Optimised ANSI C code + + +// Defines: +// Add any additional defines you need + + +#define DIR_ENCRYPT 0 // Are we encrpyting? +#define DIR_DECRYPT 1 // Are we decrpyting? +#define MODE_ECB 1 // Are we ciphering in ECB mode? +#define MODE_CBC 2 // Are we ciphering in CBC mode? +#define MODE_CFB1 3 // Are we ciphering in 1-bit CFB mode? +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#define BITSPERBLOCK 128 // Default number of bits in a cipher block + +// Error Codes - CHANGE POSSIBLE: inclusion of additional error codes +#define BAD_KEY_DIR -1 // Key direction is invalid, e.g., unknown value +#define BAD_KEY_MAT -2 // Key material not of correct length +#define BAD_KEY_INSTANCE -3 // Key passed is not valid +#define BAD_CIPHER_MODE -4 // Params struct passed to cipherInit invalid +#define BAD_CIPHER_STATE -5 // Cipher in wrong state (e.g., not initialized) +#define BAD_BLOCK_LENGTH -6 +#define BAD_CIPHER_INSTANCE -7 + + +// CHANGE POSSIBLE: inclusion of algorithm specific defines +// 14.Dec.2005 Cirilo: keys are now unsigned char rather than hex (ASCII) +#define MAX_KEY_SIZE 32 // # of unsigned char's needed to represent a key +#define MAX_IV_SIZE 16 // # bytes needed to represent an IV + +// Typedefs: +// Typedef'ed data storage elements. Add any algorithm specific parameters at the bottom of the structs as appropriate. + +typedef unsigned char BYTE; + +// The structure for key information +typedef struct { + BYTE direction; /// Key used for encrypting or decrypting? + int keyLen; /// Length of the key + char keyMaterial[MAX_KEY_SIZE+1]; /// Raw key data in ASCII, e.g., user input or KAT values + /// The following parameters are algorithm dependent, replace or add as necessary + int blockLen; /// block length + word8 keySched[MAXROUNDS+1][4][4]; /// key schedule + } keyInstance; + +// The structure for cipher information +typedef struct { // changed order of the components + BYTE mode; /// MODE_ECB, MODE_CBC, or MODE_CFB1 + BYTE IV[MAX_IV_SIZE]; /// A possible Initialization Vector for ciphering + // Add any algorithm specific parameters needed here + int blockLen; /// Sample: Handles non-128 bit block sizes (if available) + } cipherInstance; + + +// Function protoypes +// CHANGED: makeKey(): parameter blockLen added this parameter is absolutely necessary if you want to +// setup the round keys in a variable block length setting +// cipherInit(): parameter blockLen added (for obvious reasons) + +int makeKey(keyInstance *key, BYTE direction, int keyLen, char *keyMaterial); + +int cipherInit(cipherInstance *cipher, BYTE mode, char *IV); + +int blockEncrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, + int inputLen, BYTE *outBuffer); + +int blockDecrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, + int inputLen, BYTE *outBuffer); +int cipherUpdateRounds(cipherInstance *cipher, keyInstance *key, BYTE *input, + int inputLen, BYTE *outBuffer, int Rounds); + + +#endif // __RIJNDAEL_API_H diff --git a/Multiplayer/raknet/Router.h b/Multiplayer/raknet/Router.h new file mode 100644 index 000000000..cb79a50c5 --- /dev/null +++ b/Multiplayer/raknet/Router.h @@ -0,0 +1,97 @@ +/// \file +/// \brief Router plugin. Allows you to send to systems you are not directly connected to, and to route those messages +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __ROUTER_PLUGIN_H +#define __ROUTER_PLUGIN_H + +class RakPeerInterface; +#include "RakNetTypes.h" +#include "PluginInterface.h" +#include "DS_OrderedList.h" +#include "DS_WeightedGraph.h" +#include "PacketPriority.h" +#include "SystemAddressList.h" +#include "RouterInterface.h" +#include "Export.h" +#include "ConnectionGraph.h" + +/// \defgroup ROUTER_GROUP Router +/// \ingroup PLUGINS_GROUP + +/// \ingroup ROUTER_GROUP +/// \brief Used to route messages between peers +class RAK_DLL_EXPORT Router : public PluginInterface , public RouterInterface +{ +public: + Router(); + virtual ~Router(); + + // -------------------------------------------------------------------------------------------- + // User functions + // -------------------------------------------------------------------------------------------- + /// We can restrict what kind of messages are routed by this plugin. + /// This is useful for security, since you usually want to restrict what kind of messages you have to worry about from (as an example) other + /// clients in a client / server system + /// \param[in] restrict True to restrict what messages will be routed. False to not do so (default). + void SetRestrictRoutingByType(bool restrict__); + + /// If types are restricted, this adds an allowed message type to be routed + /// \param[in] messageId The type to not allow routing of. + void AddAllowedType(unsigned char messageId); + + /// Removes a restricted type previously added with AddRestrictedType + /// \param[in] messageId The type to no longer restrict routing of. + void RemoveAllowedType(unsigned char messageId); + + /// Set the connection graph, which is a weighted graph of the topology of the network. You can easily get this from the + /// ConnectionGraph plugin. See the 'router' sample for usage. + /// This is necessary if you want to send (not necessary just to route). + /// \param[in] connectionGraph A weighted graph representing the topology of the network. + void SetConnectionGraph(DataStructures::WeightedGraph *connectionGraph); + + /// Sends a bitstream to one or more systems. If multiple systems are specified, the message will be multicasted using a minimum spanning tree + /// \pre You just have called SetConnectionGraph with a valid graph representing the network topology + /// \note Single target sends from RakPeer with this plugin installed will also be routed. Sends from other plugins will also be routed as long as this plugin is attached first. + /// \param[in] data The data to send + /// \param[in] bitLength How many bits long data is + /// \param[in] priority What priority level to send on. + /// \param[in] reliability How reliability to send this data + /// \param[in] orderingChannel When using ordered or sequenced packets, what channel to order these on.- Packets are only ordered relative to other packets on the same stream + /// \param[in] recipients A list of recipients to send to. To send to one recipient, just pass a SystemAddress + /// \return True on success, false mostly if the connection graph cannot find the destination. + bool Send( char *data, BitSize_t bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddressList *recipients ); + bool Send( const char *data, BitSize_t bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress ); + + // -------------------------------------------------------------------------------------------- + // Packet handling functions + // -------------------------------------------------------------------------------------------- + virtual void OnAttach(RakPeerInterface *peer); + virtual void OnDetach(RakPeerInterface *peer); + virtual void OnShutdown(RakPeerInterface *peer); + virtual void Update(RakPeerInterface *peer); + virtual PluginReceiveResult OnReceive(RakPeerInterface *peer, Packet *packet); + virtual void OnCloseConnection(RakPeerInterface *peer, SystemAddress systemAddress); +protected: + void SendTree(PacketPriority priority, PacketReliability reliability, char orderingChannel, DataStructures::Tree *tree, const char *data, BitSize_t bitLength, RakNet::BitStream *out, SystemAddressList *recipients); + void SerializePreorder(DataStructures::Tree *tree, RakNet::BitStream *out, SystemAddressList *recipients) const; + DataStructures::WeightedGraph *graph; + bool restrictByType; + DataStructures::OrderedList allowedTypes; + RakPeerInterface *rakPeer; +}; + +#endif diff --git a/Multiplayer/raknet/RouterInterface.h b/Multiplayer/raknet/RouterInterface.h new file mode 100644 index 000000000..f56ef3714 --- /dev/null +++ b/Multiplayer/raknet/RouterInterface.h @@ -0,0 +1,17 @@ +#ifndef __ROUTER_INTERFACE_H +#define __ROUTER_INTERFACE_H + +#include "Export.h" +#include "RakNetTypes.h" + +/// On failed directed sends, RakNet can call an alternative send function to use. +class RAK_DLL_EXPORT RouterInterface +{ +public: + RouterInterface() {} + virtual ~RouterInterface() {} + + virtual bool Send( const char *data, BitSize_t bitLength, PacketPriority priority, PacketReliability reliability, char orderingChannel, SystemAddress systemAddress )=0; +}; + +#endif diff --git a/Multiplayer/raknet/SHA1.h b/Multiplayer/raknet/SHA1.h new file mode 100644 index 000000000..6b7efc10b --- /dev/null +++ b/Multiplayer/raknet/SHA1.h @@ -0,0 +1,87 @@ +/// \brief \b [Internal] SHA-1 computation class +/// +/// 100% free public domain implementation of the SHA-1 +/// algorithm by Dominik Reichl +/// +/// +/// === Test Vectors (from FIPS PUB 180-1) === +/// +/// "abc" +/// A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +/// +/// "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +/// 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +/// +/// A million repetitions of "a" +/// 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F + +#ifndef ___SHA1_H___ +#define ___SHA1_H___ + +#include "RakMemoryOverride.h" +#include // Needed for file access +#if !defined(_PS3) && !defined(__PS3__) && !defined(SN_TARGET_PS3) +#include // Needed for memset and memcpy +#endif +#include // Needed for strcat and strcpy +#include "Export.h" + +#define MAX_FILE_READ_BUFFER 8000 + +#define SHA1_LENGTH 20 + +class RAK_DLL_EXPORT CSHA1 +{ + +public: + // Rotate x bits to the left + #define ROL32(value, bits) (((value)<<(bits))|((value)>>(32-(bits)))) + +#ifdef LITTLE_ENDIAN +#define SHABLK0(i) (block->l[i] = (ROL32(block->l[i],24) & 0xFF00FF00) \ + | (ROL32(block->l[i],8) & 0x00FF00FF)) +#else +#define SHABLK0(i) (block->l[i]) +#endif + +#define SHABLK(i) (block->l[i&15] = ROL32(block->l[(i+13)&15] ^ block->l[(i+8)&15] \ + ^ block->l[(i+2)&15] ^ block->l[i&15],1)) + + // SHA-1 rounds +#define R0(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK0(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define R1(v,w,x,y,z,i) { z+=((w&(x^y))^y)+SHABLK(i)+0x5A827999+ROL32(v,5); w=ROL32(w,30); } +#define R2(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0x6ED9EBA1+ROL32(v,5); w=ROL32(w,30); } +#define R3(v,w,x,y,z,i) { z+=(((w|x)&y)|(w&x))+SHABLK(i)+0x8F1BBCDC+ROL32(v,5); w=ROL32(w,30); } +#define R4(v,w,x,y,z,i) { z+=(w^x^y)+SHABLK(i)+0xCA62C1D6+ROL32(v,5); w=ROL32(w,30); } + + typedef union { + unsigned char c[ 64 ]; + unsigned int l[ 16 ]; + } SHA1_WORKSPACE_BLOCK; + /* Two different formats for ReportHash(...) + */ + enum { REPORT_HEX = 0, + REPORT_DIGIT = 1}; + + CSHA1(); + virtual ~CSHA1(); + + unsigned int m_state[ 5 ]; + unsigned int m_count[ 2 ]; + unsigned char m_buffer[ 64 ]; + unsigned char m_digest[ 20 ]; + void Reset(); + void Update( unsigned char* data, unsigned int len ); + bool HashFile( char *szFileName ); + void Final(); + void ReportHash( char *szReport, unsigned char uReportType = REPORT_HEX ); + void GetHash( unsigned char *uDest ); + unsigned char * GetHash( void ) const; + +private: + void Transform( unsigned int state[ 5 ], unsigned char buffer[ 64 ] ); + unsigned char workspace[ 64 ]; +}; + +#endif // ___SHA1_H___ + diff --git a/Multiplayer/raknet/SimpleMutex.h b/Multiplayer/raknet/SimpleMutex.h new file mode 100644 index 000000000..2f9f8437e --- /dev/null +++ b/Multiplayer/raknet/SimpleMutex.h @@ -0,0 +1,61 @@ +/// \file +/// \brief \b [Internal] Encapsulates a mutex +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __SIMPLE_MUTEX_H +#define __SIMPLE_MUTEX_H + +#include "RakMemoryOverride.h" +#if defined(_XBOX) || defined(X360) +#include "XBOX360Includes.h" +#elif defined(_WIN32) +#include +#else +#include +#include +#endif +#include "Export.h" +/// \brief An easy to use mutex. +/// +/// I wrote this because the version that comes with Windows is too complicated and requires too much code to use. +/// @remark Previously I used this everywhere, and in fact for a year or two RakNet was totally threadsafe. While doing profiling, I saw that this function was incredibly slow compared to the blazing performance of everything else, so switched to single producer / consumer everywhere. Now the user thread of RakNet is not threadsafe, but it's 100X faster than before. +class RAK_DLL_EXPORT SimpleMutex +{ +public: + + /// Constructor + SimpleMutex(); + + // Destructor + ~SimpleMutex(); + + // Locks the mutex. Slow! + void Lock(void); + + // Unlocks the mutex. + void Unlock(void); +private: + void Init(void); + #ifdef _WIN32 + CRITICAL_SECTION criticalSection; /// Docs say this is faster than a mutex for single process access + #else + pthread_mutex_t hMutex; + #endif + bool isInitialized; +}; + +#endif + diff --git a/Multiplayer/raknet/SimpleTCPServer.h b/Multiplayer/raknet/SimpleTCPServer.h new file mode 100644 index 000000000..1181cd0db --- /dev/null +++ b/Multiplayer/raknet/SimpleTCPServer.h @@ -0,0 +1 @@ +// Eraseme \ No newline at end of file diff --git a/Multiplayer/raknet/SingleProducerConsumer.h b/Multiplayer/raknet/SingleProducerConsumer.h new file mode 100644 index 000000000..a3b338c1c --- /dev/null +++ b/Multiplayer/raknet/SingleProducerConsumer.h @@ -0,0 +1,352 @@ +/// \file +/// \brief \b [Internal] Passes queued data between threads using a circular buffer with read and write pointers +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __SINGLE_PRODUCER_CONSUMER_H +#define __SINGLE_PRODUCER_CONSUMER_H + +#include "RakAssert.h" + +static const int MINIMUM_LIST_SIZE=8; + +#include "RakMemoryOverride.h" +#include "Export.h" + +/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures +/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish. +namespace DataStructures +{ + /// \brief A single producer consumer implementation without critical sections. + template + class RAK_DLL_EXPORT SingleProducerConsumer + { + public: + /// Constructor + SingleProducerConsumer(); + + /// Destructor + ~SingleProducerConsumer(); + + /// WriteLock must be immediately followed by WriteUnlock. These two functions must be called in the same thread. + /// \return A pointer to a block of data you can write to. + SingleProducerConsumerType* WriteLock(void); + + /// Call if you don't want to write to a block of data from WriteLock() after all. + /// Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// \param[in] cancelToLocation Which WriteLock() to cancel. + void CancelWriteLock(SingleProducerConsumerType* cancelToLocation); + + /// Call when you are done writing to a block of memory returned by WriteLock() + void WriteUnlock(void); + + /// ReadLock must be immediately followed by ReadUnlock. These two functions must be called in the same thread. + /// \retval 0 No data is availble to read + /// \retval Non-zero The data previously written to, in another thread, by WriteLock followed by WriteUnlock. + SingleProducerConsumerType* ReadLock(void); + + // Cancelling locks cancels all locks back up to the data passed. So if you lock twice and cancel using the first lock, the second lock is ignored + /// param[in] Which ReadLock() to cancel. + void CancelReadLock(SingleProducerConsumerType* cancelToLocation); + + /// Signals that we are done reading the the data from the least recent call of ReadLock. + /// At this point that pointer is no longer valid, and should no longer be read. + void ReadUnlock(void); + + /// Clear is not thread-safe and none of the lock or unlock functions should be called while it is running. + void Clear(void); + + /// This function will estimate how many elements are waiting to be read. It's threadsafe enough that the value returned is stable, but not threadsafe enough to give accurate results. + /// \return An ESTIMATE of how many data elements are waiting to be read + int Size(void) const; + + /// Make sure that the pointer we done reading for the call to ReadUnlock is the right pointer. + /// param[in] A previous pointer returned by ReadLock() + bool CheckReadUnlockOrder(const SingleProducerConsumerType* data) const; + + /// Returns if ReadUnlock was called before ReadLock + /// \return If the read is locked + bool ReadIsLocked(void) const; + + private: + struct DataPlusPtr + { + DataPlusPtr () {readyToRead=false;} + SingleProducerConsumerType object; + + // Ready to read is so we can use an equality boolean comparison, in case the writePointer var is trashed while context switching. + volatile bool readyToRead; + volatile DataPlusPtr *next; + }; + volatile DataPlusPtr *readAheadPointer; + volatile DataPlusPtr *writeAheadPointer; + volatile DataPlusPtr *readPointer; + volatile DataPlusPtr *writePointer; + unsigned readCount, writeCount; + }; + + template + SingleProducerConsumer::SingleProducerConsumer() + { + // Preallocate + readPointer = RakNet::OP_NEW(); + writePointer=readPointer; + readPointer->next = RakNet::OP_NEW(); + int listSize; +#ifdef _DEBUG + RakAssert(MINIMUM_LIST_SIZE>=3); +#endif + for (listSize=2; listSize < MINIMUM_LIST_SIZE; listSize++) + { + readPointer=readPointer->next; + readPointer->next = RakNet::OP_NEW(); + } + readPointer->next->next=writePointer; // last to next = start + readPointer=writePointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + SingleProducerConsumer::~SingleProducerConsumer() + { + volatile DataPlusPtr *next; + readPointer=writeAheadPointer->next; + while (readPointer!=writeAheadPointer) + { + next=readPointer->next; + RakNet::OP_DELETE((char*) readPointer); + readPointer=next; + } + RakNet::OP_DELETE((char*) readPointer); + } + + template + SingleProducerConsumerType* SingleProducerConsumer::WriteLock( void ) + { + if (writeAheadPointer->next==readPointer || + writeAheadPointer->next->readyToRead==true) + { + volatile DataPlusPtr *originalNext=writeAheadPointer->next; + writeAheadPointer->next=new DataPlusPtr; + RakAssert(writeAheadPointer->next); + writeAheadPointer->next->next=originalNext; + } + + volatile DataPlusPtr *last; + last=writeAheadPointer; + writeAheadPointer=writeAheadPointer->next; + + return (SingleProducerConsumerType*) last; + } + + template + void SingleProducerConsumer::CancelWriteLock( SingleProducerConsumerType* cancelToLocation ) + { + writeAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::WriteUnlock( void ) + { + // DataPlusPtr *dataContainer = (DataPlusPtr *)structure; + +#ifdef _DEBUG + RakAssert(writePointer->next!=readPointer); + RakAssert(writePointer!=writeAheadPointer); +#endif + + writeCount++; + // User is done with the data, allow send by updating the write pointer + writePointer->readyToRead=true; + writePointer=writePointer->next; + } + + template + SingleProducerConsumerType* SingleProducerConsumer::ReadLock( void ) + { + if (readAheadPointer==writePointer || + readAheadPointer->readyToRead==false) + { + return 0; + } + + volatile DataPlusPtr *last; + last=readAheadPointer; + readAheadPointer=readAheadPointer->next; + return (SingleProducerConsumerType*)last; + } + + template + void SingleProducerConsumer::CancelReadLock( SingleProducerConsumerType* cancelToLocation ) + { +#ifdef _DEBUG + RakAssert(readPointer!=writePointer); +#endif + readAheadPointer=(DataPlusPtr *)cancelToLocation; + } + + template + void SingleProducerConsumer::ReadUnlock( void ) + { +#ifdef _DEBUG + RakAssert(readAheadPointer!=readPointer); // If hits, then called ReadUnlock before ReadLock + RakAssert(readPointer!=writePointer); // If hits, then called ReadUnlock when Read returns 0 +#endif + readCount++; + + // Allow writes to this memory block + readPointer->readyToRead=false; + readPointer=readPointer->next; + } + + template + void SingleProducerConsumer::Clear( void ) + { + // Shrink the list down to MINIMUM_LIST_SIZE elements + volatile DataPlusPtr *next; + writePointer=readPointer->next; + + int listSize=1; + next=readPointer->next; + while (next!=readPointer) + { + listSize++; + next=next->next; + } + + while (listSize-- > MINIMUM_LIST_SIZE) + { + next=writePointer->next; +#ifdef _DEBUG + RakAssert(writePointer!=readPointer); +#endif + RakNet::OP_DELETE((char*) writePointer); + writePointer=next; + } + + readPointer->next=writePointer; + writePointer=readPointer; + readAheadPointer=readPointer; + writeAheadPointer=writePointer; + readCount=writeCount=0; + } + + template + int SingleProducerConsumer::Size( void ) const + { + return writeCount-readCount; + } + + template + bool SingleProducerConsumer::CheckReadUnlockOrder(const SingleProducerConsumerType* data) const + { + return const_cast(&readPointer->object) == data; + } + + + template + bool SingleProducerConsumer::ReadIsLocked(void) const + { + return readAheadPointer!=readPointer; + } +} + +#endif + +/* +#include "SingleProducerConsumer.h" +#include +#include "RakAssert.h" +#include +#include +#if defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +#include +#else +#include +#endif +#include + +#define READ_COUNT_ITERATIONS 10000000 + +DataStructures::SingleProducerConsumer spc; +unsigned long readCount; + +unsigned __stdcall ProducerThread( LPVOID arguments ) +{ +unsigned long producerCount; +unsigned long *writeBlock; +producerCount=0; +while (readCount < READ_COUNT_ITERATIONS) +{ +writeBlock=spc.WriteLock(); +*writeBlock=producerCount; +spc.WriteUnlock(); +producerCount++; +if ((producerCount%1000000)==0) +{ +RAKNET_DEBUG_PRINTF("WriteCount: %i. BufferSize=%i\n", producerCount, spc.Size()); +} +} +RAKNET_DEBUG_PRINTF("PRODUCER THREAD ENDED!\n"); +return 0; +} + +unsigned __stdcall ConsumerThread( LPVOID arguments ) +{ +unsigned long *readBlock; +while (readCount < READ_COUNT_ITERATIONS) +{ +if ((readBlock=spc.ReadLock())!=0) +{ +if (*readBlock!=readCount) +{ +RAKNET_DEBUG_PRINTF("Test failed! Expected %i got %i!\n", readCount, *readBlock); +readCount = READ_COUNT_ITERATIONS; +RakAssert(0); +} +spc.ReadUnlock(); +readCount++; +if ((readCount%1000000)==0) +{ +RAKNET_DEBUG_PRINTF("ReadCount: %i. BufferSize=%i\n", readCount, spc.Size()); +} +} +} +RAKNET_DEBUG_PRINTF("CONSUMER THREAD ENDED!\n"); +return 0; +} + +void main(void) +{ +readCount=0; +unsigned threadId1 = 0; +unsigned threadId2 = 0; +HANDLE thread1Handle, thread2Handle; +unsigned long startTime = timeGetTime(); + +thread1Handle=(HANDLE)_beginthreadex( NULL, 0, ProducerThread, 0, 0, &threadId1 ); +thread2Handle=(HANDLE)_beginthreadex( NULL, 0, ConsumerThread, 0, 0, &threadId1 ); + +while (readCount < READ_COUNT_ITERATIONS) +{ +Sleep(0); +} +char str[256]; +RAKNET_DEBUG_PRINTF("Elapsed time = %i milliseconds. Press Enter to continue\n", timeGetTime() - startTime); +fgets(str, sizeof(str), stdin); +} +*/ diff --git a/Multiplayer/raknet/SocketLayer.h b/Multiplayer/raknet/SocketLayer.h new file mode 100644 index 000000000..3e46b3b46 --- /dev/null +++ b/Multiplayer/raknet/SocketLayer.h @@ -0,0 +1,159 @@ +/// \file +/// \brief SocketLayer class implementation +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + + +#ifndef __SOCKET_LAYER_H +#define __SOCKET_LAYER_H + +#include "RakMemoryOverride.h" +#if defined(_XBOX) || defined(X360) +#include "XBOX360Includes.h" +#elif defined(_PS3) || defined(__PS3__) || defined(SN_TARGET_PS3) +#include "PS3Includes.h" +typedef int SOCKET; +#elif defined(_XBOX) || defined(X360) +#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 +#else +#include +#include +#include +#include +#include +#include +//#include "RakMemoryOverride.h" +/// Unix/Linux uses ints for sockets +typedef int SOCKET; +#endif +//#include "ClientContextStruct.h" + +class RakPeer; + +// A platform independent implementation of Berkeley sockets, with settings used by RakNet +class SocketLayer +{ + +public: + + /// Default Constructor + SocketLayer(); + + /// Destructor + ~SocketLayer(); + + // Get the singleton instance of the Socket Layer. + /// \return unique instance + static inline SocketLayer* Instance() + { + return & I; + } + + // Connect a socket to a remote host. + /// \param[in] writeSocket The local socket. + /// \param[in] binaryAddress The address of the remote host. + /// \param[in] port the remote port. + /// \return A new socket used for communication. + SOCKET Connect( SOCKET writeSocket, unsigned int binaryAddress, unsigned short port ); + + /// Creates a bound socket to listen for incoming connections on the specified port + /// \param[in] port the port number + /// \param[in] blockingSocket + /// \return A new socket used for accepting clients + SOCKET CreateBoundSocket( unsigned short port, bool blockingSocket, const char *forceHostAddress ); + SOCKET CreateBoundSocket_PS3Lobby( unsigned short port, bool blockingSocket, const char *forceHostAddress ); + + /// Returns if this specified port is in use, for UDP + /// \param[in] port the port number + /// \return If this port is already in use + static bool IsPortInUse(unsigned short port); + + #if !defined(_XBOX) && !defined(X360) + const char* DomainNameToIP( const char *domainName ); + #endif + + /// Start an asynchronous read using the specified socket. The callback will use the specified SystemAddress (associated with this socket) and call either the client or the server callback (one or + /// the other should be 0) + /// \note Was used for depreciated IO completion ports. + bool AssociateSocketWithCompletionPortAndRead( SOCKET readSocket, unsigned int binaryAddress, unsigned short port, RakPeer* rakPeer ); + + /// Write \a data of length \a length to \a writeSocket + /// \param[in] writeSocket The socket to write to + /// \param[in] data The data to write + /// \param[in] length The length of \a data + void Write( const SOCKET writeSocket, const char* data, const int length ); + + /// Read data from a socket + /// \param[in] s the socket + /// \param[in] rakPeer The instance of rakPeer containing the recvFrom C callback + /// \param[in] errorCode An error code if an error occured . + /// \param[in] connectionSocketIndex Which of the sockets in RakPeer we are using + /// \return Returns true if you successfully read data, false on error. + int RecvFrom( const SOCKET s, RakPeer *rakPeer, int *errorCode, unsigned connectionSocketIndex, bool isPs3LobbySocket ); + +#if !defined(_XBOX) && !defined(_X360) + /// Retrieve all local IP address in a string format. + /// \param[in] s The socket whose port we are referring to + /// \param[in] ipList An array of ip address in dotted notation. + void GetMyIP( char ipList[ MAXIMUM_NUMBER_OF_INTERNAL_IDS ][ 16 ] ); +#endif + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. + int SendTo( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, bool isPs3LobbySocket ); + + /// Call sendto (UDP obviously) + /// It won't reach the recipient, except on a LAN + /// However, this is good for opening routers / firewalls + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] ip The address of the remote host in dotted notation. + /// \param[in] port The port number to send to. + /// \param[in] ttl Max hops of datagram + /// \return 0 on success, nonzero on failure. + int SendToTTL( SOCKET s, const char *data, int length, const char ip[ 16 ], unsigned short port, int ttl ); + + /// Call sendto (UDP obviously) + /// \param[in] s the socket + /// \param[in] data The byte buffer to send + /// \param[in] length The length of the \a data in bytes + /// \param[in] binaryAddress The address of the remote host in binary format. + /// \param[in] port The port number to send to. + /// \return 0 on success, nonzero on failure. + int SendTo( SOCKET s, const char *data, int length, unsigned int binaryAddress, unsigned short port, bool isPs3LobbySocket ); + + /// Returns the local port, useful when passing 0 as the startup port. + /// \param[in] s The socket whose port we are referring to + /// \return The local port + unsigned short GetLocalPort ( SOCKET s ); + +private: + + + static SocketLayer I; + void SetSocketOptions( SOCKET listenSocket); +}; + +#endif + diff --git a/Multiplayer/raknet/StringCompressor.h b/Multiplayer/raknet/StringCompressor.h new file mode 100644 index 000000000..27f4a690e --- /dev/null +++ b/Multiplayer/raknet/StringCompressor.h @@ -0,0 +1,111 @@ +/// \file +/// \brief \b Compresses/Decompresses ASCII strings and writes/reads them to BitStream class instances. You can use this to easily serialize and deserialize your own strings. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __STRING_COMPRESSOR_H +#define __STRING_COMPRESSOR_H + +#include "Export.h" +#include "DS_Map.h" +#include "RakMemoryOverride.h" + + +//#include + +/// Forward declaration +namespace RakNet +{ + class BitStream; + class RakString; +}; + +class HuffmanEncodingTree; + +/// \brief Writes and reads strings to and from bitstreams. +/// +/// Only works with ASCII strings. The default compression is for English. +/// You can call GenerateTreeFromStrings to compress and decompress other languages efficiently as well. +class RAK_DLL_EXPORT StringCompressor +{ +public: + + /// Destructor + ~StringCompressor(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringCompressor + static StringCompressor* Instance(void); + + /// Given an array of strings, such as a chat log, generate the optimal encoding tree for it. + /// This function is optional and if it is not called a default tree will be used instead. + /// \param[in] input An array of bytes which should point to text. + /// \param[in] inputLength Length of \a input + /// \param[in] languageID An identifier for the language / string table to generate the tree for. English is automatically created with ID 0 in the constructor. + void GenerateTreeFromStrings( unsigned char *input, unsigned inputLength, int languageID ); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The max number of bytes to write of \a input. Use 0 to mean no limit. + /// \param[out] output The bitstream to write the compressed string to + /// \param[in] languageID Which language to use + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output, int languageID=0 ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + /// \param[in] languageID Which language to use + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID=0 ); + +#ifdef _CSTRING_COMPRESSOR + void EncodeString( const CString &input, int maxCharsToWrite, RakNet::BitStream *output, int languageID=0 ); + bool DecodeString( CString &output, int maxCharsToWrite, RakNet::BitStream *input, int languageID=0 ); +#endif + +#ifdef _STD_STRING_COMPRESSOR + void EncodeString( const std::string &input, int maxCharsToWrite, RakNet::BitStream *output, int languageID=0 ); + bool DecodeString( std::string *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID=0 ); +#endif + + void EncodeString( const RakNet::RakString *input, int maxCharsToWrite, RakNet::BitStream *output, int languageID=0 ); + bool DecodeString( RakNet::RakString *output, int maxCharsToWrite, RakNet::BitStream *input, int languageID=0 ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + /// Private Constructor + StringCompressor(); + +private: + + /// Singleton instance + static StringCompressor *instance; + + /// Pointer to the huffman encoding trees. + DataStructures::Map huffmanEncodingTrees; + + static int referenceCount; +}; + +/// Define to more easily reference the string compressor instance. +/// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. +#define stringCompressor StringCompressor::Instance() + +#endif diff --git a/Multiplayer/raknet/StringTable.h b/Multiplayer/raknet/StringTable.h new file mode 100644 index 000000000..543c953d8 --- /dev/null +++ b/Multiplayer/raknet/StringTable.h @@ -0,0 +1,104 @@ +/// \file +/// \brief A simple class to encode and decode known strings based on a lookup table. Similar to the StringCompressor class. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __STRING_TABLE_H +#define __STRING_TABLE_H + +#include "DS_OrderedList.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +/// Forward declaration +namespace RakNet +{ + class BitStream; +}; + +/// StringTableType should be the smallest type possible, or else it defeats the purpose of the StringTable class, which is to save bandwidth. +typedef unsigned char StringTableType; + +/// The string plus a bool telling us if this string was copied or not. +struct StrAndBool +{ + char *str; + bool b; +}; +int RAK_DLL_EXPORT StrAndBoolComp( char *const &key, const StrAndBool &data ); + +namespace RakNet +{ + /// This is an even more efficient alternative to StringCompressor in that it writes a single byte from a lookup table and only does compression + /// if the string does not already exist in the table. + /// All string tables must match on all systems - hence you must add all the strings in the same order on all systems. + /// Furthermore, this must be done before sending packets that use this class, since the strings are ordered for fast lookup. Adding after that time would mess up all the indices so don't do it. + /// Don't use this class to write strings which were not previously registered with AddString, since you just waste bandwidth then. Use StringCompressor instead. + /// \brief Writes a string index, instead of the whole string + class RAK_DLL_EXPORT StringTable + { + public: + + /// Destructor + ~StringTable(); + + /// static function because only static functions can access static members + /// The RakPeer constructor adds a reference to this class, so don't call this until an instance of RakPeer exists, or unless you call AddReference yourself. + /// \return the unique instance of the StringTable + static StringTable* Instance(void); + + /// Add a string to the string table. + /// \param[in] str The string to add to the string table + /// \param[in] copyString true to make a copy of the passed string (takes more memory), false to not do so (if your string is in static memory). + void AddString(const char *str, bool copyString); + + /// Writes input to output, compressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[in] input Pointer to an ASCII string + /// \param[in] maxCharsToWrite The size of \a input + /// \param[out] output The bitstream to write the compressed string to + void EncodeString( const char *input, int maxCharsToWrite, RakNet::BitStream *output ); + + /// Writes input to output, uncompressed. Takes care of the null terminator for you. + /// Relies on the StringCompressor class, which is automatically reference counted in the constructor and destructor in RakPeer. You can call the reference counting functions yourself if you wish too. + /// \param[out] output A block of bytes to receive the output + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + /// \param[in] input The bitstream containing the compressed string + bool DecodeString( char *output, int maxCharsToWrite, RakNet::BitStream *input ); + + /// Used so I can allocate and deallocate this singleton at runtime + static void AddReference(void); + + /// Used so I can allocate and deallocate this singleton at runtime + static void RemoveReference(void); + + /// Private Constructor + StringTable(); + + protected: + /// Called when you mess up and send a string using this class that was not registered with AddString + /// \param[in] maxCharsToWrite Size, in bytes, of \a output . A NULL terminator will always be appended to the output string. If the maxCharsToWrite is not large enough, the string will be truncated. + void LogStringNotFound(const char *strName); + + /// Singleton instance + static StringTable *instance; + static int referenceCount; + + DataStructures::OrderedList orderedStringList; + }; +} + + +#endif diff --git a/Multiplayer/raknet/SuperFastHash.h b/Multiplayer/raknet/SuperFastHash.h new file mode 100644 index 000000000..318c83a07 --- /dev/null +++ b/Multiplayer/raknet/SuperFastHash.h @@ -0,0 +1,16 @@ +#ifndef __SUPER_FAST_HASH_H +#define __SUPER_FAST_HASH_H + +#include + +// From http://www.azillionmonkeys.com/qed/hash.html +// Author of main code is Paul Hsieh +// I just added some convenience functions +// Also note http://burtleburtle.net/bob/hash/doobs.html, which shows that this is 20% faster than the one on that page but has more collisions + +unsigned int SuperFastHash (const char * data, int length); +unsigned int SuperFastHashIncremental (const char * data, int len, unsigned int lastHash ); +unsigned int SuperFastHashFile (const char * filename); +unsigned int SuperFastHashFilePtr (FILE *fp); + +#endif diff --git a/Multiplayer/raknet/SystemAddressList.h b/Multiplayer/raknet/SystemAddressList.h new file mode 100644 index 000000000..5a7e941ec --- /dev/null +++ b/Multiplayer/raknet/SystemAddressList.h @@ -0,0 +1,46 @@ +/// \file +/// \brief Just a class to hold a list of systems +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __SYSTEM_ID_LIST_H +#define __SYSTEM_ID_LIST_H + +#include "RakNetTypes.h" +#include "DS_OrderedList.h" + +class SystemAddressList +{ +public: + SystemAddressList(); + SystemAddressList(SystemAddress system); + void AddSystem(SystemAddress system); + void RandomizeOrder(void); + void Serialize(RakNet::BitStream *out); + bool Deserialize(RakNet::BitStream *in); + bool Save(const char *filename); + bool Load(const char *filename); + void RemoveSystem(SystemAddress system); + unsigned Size(void) const; + SystemAddress& operator[] ( const unsigned int position ) const; + void Clear(void); + + DataStructures::List * GetList(void); + +protected: + DataStructures::List systemList; +}; + +#endif diff --git a/Multiplayer/raknet/TCPInterface.h b/Multiplayer/raknet/TCPInterface.h new file mode 100644 index 000000000..08ca8aebd --- /dev/null +++ b/Multiplayer/raknet/TCPInterface.h @@ -0,0 +1,173 @@ +/// \file +/// \brief A simple TCP based server allowing sends and receives. Can be connected by any TCP client, including 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.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __SIMPLE_TCP_SERVER +#define __SIMPLE_TCP_SERVER + +#if defined(_XBOX) || defined(X360) +#elif defined(_WIN32) +#include +#include +#else +#include +#include +#include // fd_set +#include +#include +#include +#include +#include +/// Unix/Linux uses ints for sockets +typedef unsigned int SOCKET; +#endif + +#include "RakMemoryOverride.h" +#include "DS_List.h" +#include "RakNetTypes.h" +#include "SingleProducerConsumer.h" +#include "Export.h" +#include "RakThread.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "RakNetDefines.h" + +struct RemoteClient; + +#if defined(OPEN_SSL_CLIENT_SUPPORT) +#include +#include +#include +#include +#include +#endif + +/// \internal +/// \brief As the name says, a simple multithreaded TCP server. Used by TelnetTransport +class RAK_DLL_EXPORT TCPInterface +{ +public: + TCPInterface(); + ~TCPInterface(); + + /// Starts the TCP server on the indicated port + bool Start(unsigned short port, unsigned short maxIncomingConnections); + + /// Stops the TCP server + void Stop(void); + + /// Connect to the specified host on the specified port + SystemAddress Connect(const char* host, unsigned short remotePort, bool block=true); + +#if defined(OPEN_SSL_CLIENT_SUPPORT) + /// Start SSL on an existing connection, notified with HasCompletedConnectionAttempt + void StartSSLClient(SystemAddress systemAddress); + + /// Was SSL started on this socket? + bool IsSSLActive(SystemAddress systemAddress); +#endif + + /// Sends a byte stream + void Send( const char *data, unsigned length, SystemAddress systemAddress ); + + /// Returns data received + Packet* Receive( void ); + + /// Disconnects a player/address + void CloseConnection( SystemAddress systemAddress ); + + /// Deallocates a packet returned by Receive + void DeallocatePacket( Packet *packet ); + + /// Has a previous call to connect succeeded? Only used if block==false in the call to Connect + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasCompletedConnectionAttempt(void); + + /// Has a previous call to connect failed? Only used if block==false in the call to Connect + /// \return UNASSIGNED_SYSTEM_ADDRESS = no. Anything else means yes. + SystemAddress HasFailedConnectionAttempt(void); + + /// Queued events of new connections + SystemAddress HasNewConnection(void); + + /// Queued events of lost connections + SystemAddress HasLostConnection(void); +protected: + + bool isStarted, threadRunning; + SOCKET listenSocket; + + // Assuming remoteClients is only used by one thread! + DataStructures::List remoteClients; + // Use this thread-safe queue to add to remoteClients + DataStructures::Queue remoteClientsInsertionQueue; + SimpleMutex remoteClientsInsertionQueueMutex; + DataStructures::SingleProducerConsumer outgoingMessages, incomingMessages; + DataStructures::SingleProducerConsumer newConnections, lostConnections, requestedCloseConnections; + DataStructures::SingleProducerConsumer newRemoteClients; + SimpleMutex completedConnectionAttemptMutex, failedConnectionAttemptMutex; + DataStructures::Queue completedConnectionAttempts, failedConnectionAttempts; + + DataStructures::List blockingSocketList; + SimpleMutex blockingSocketListMutex; + + friend RAK_THREAD_DECLARATION(UpdateTCPInterfaceLoop); + friend RAK_THREAD_DECLARATION(ConnectionAttemptLoop); + + void DeleteRemoteClient(RemoteClient *remoteClient, fd_set *exceptionFD); + void InsertRemoteClient(RemoteClient* remoteClient); + SOCKET SocketConnect(const char* host, unsigned short remotePort); + + struct ThisPtrPlusSysAddr + { + TCPInterface *tcpInterface; + SystemAddress systemAddress; + bool useSSL; + }; + +#if defined(OPEN_SSL_CLIENT_SUPPORT) + SSL_CTX* ctx; + SSL_METHOD *meth; + DataStructures::SingleProducerConsumer startSSL; + DataStructures::List activeSSLConnections; +#endif +}; + +/// Stores information about a remote client. +struct RemoteClient +{ + RemoteClient() { +#if defined(OPEN_SSL_CLIENT_SUPPORT) + ssl=0; +#endif + } + SOCKET socket; + SystemAddress systemAddress; + +#if defined(OPEN_SSL_CLIENT_SUPPORT) + SSL* ssl; + void InitSSL(SSL_CTX* ctx, SSL_METHOD *meth); + void DisconnectSSL(void); + void FreeSSL(void); + void Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#else + void Send(const char *data, unsigned int length); + int Recv(char *data, const int dataSize); +#endif +}; + +#endif diff --git a/Multiplayer/raknet/TableSerializer.h b/Multiplayer/raknet/TableSerializer.h new file mode 100644 index 000000000..6eda20af5 --- /dev/null +++ b/Multiplayer/raknet/TableSerializer.h @@ -0,0 +1,203 @@ +#ifndef __TABLE_SERIALIZER_H +#define __TABLE_SERIALIZER_H + +#include "RakMemoryOverride.h" +#include "DS_Table.h" +#include "Export.h" + +namespace RakNet +{ + class BitStream; +} + +class RAK_DLL_EXPORT TableSerializer +{ +public: + static void SerializeTable(DataStructures::Table *in, RakNet::BitStream *out); + static bool DeserializeTable(unsigned char *serializedTable, unsigned int dataLength, DataStructures::Table *out); + static bool DeserializeTable(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out); + static void SerializeColumns(DataStructures::Table *in, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeColumns(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, DataStructures::List &columns, RakNet::BitStream *out); + static void SerializeRow(DataStructures::Table::Row *in, unsigned keyIn, DataStructures::List &columns, RakNet::BitStream *out, DataStructures::List &skipColumnIndices); + static bool DeserializeRow(RakNet::BitStream *in, DataStructures::Table *out); + static void SerializeCell(RakNet::BitStream *out, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static bool DeserializeCell(RakNet::BitStream *in, DataStructures::Table::Cell *cell, DataStructures::Table::ColumnType columnType); + static void SerializeFilterQuery(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query); + // Note that this allocates query->cell->c! + static bool DeserializeFilterQuery(RakNet::BitStream *out, DataStructures::Table::FilterQuery *query); + static void SerializeFilterQueryList(RakNet::BitStream *in, DataStructures::Table::FilterQuery *query, unsigned int numQueries, unsigned int maxQueries); + // Note that this allocates queries, cells, and query->cell->c!. Use DeallocateQueryList to free. + static bool DeserializeFilterQueryList(RakNet::BitStream *out, DataStructures::Table::FilterQuery **query, unsigned int *numQueries, unsigned int maxQueries, int allocateExtraQueries=0); + static void DeallocateQueryList(DataStructures::Table::FilterQuery *query, unsigned int numQueries); +}; + +#endif + +// Test code for the table +/* +#include "LightweightDatabaseServer.h" +#include "LightweightDatabaseClient.h" +#include "TableSerializer.h" +#include "BitStream.h" +#include "StringCompressor.h" +#include "DS_Table.h" +void main(void) +{ + DataStructures::Table table; + DataStructures::Table::Row *row; + unsigned int dummydata=12345; + + // Add columns Name (string), IP (binary), score (int), and players (int). + table.AddColumn("Name", DataStructures::Table::STRING); + table.AddColumn("IP", DataStructures::Table::BINARY); + table.AddColumn("Score", DataStructures::Table::NUMERIC); + table.AddColumn("Players", DataStructures::Table::NUMERIC); + table.AddColumn("Empty Test Column", DataStructures::Table::STRING); + RakAssert(table.GetColumnCount()==5); + row=table.AddRow(0); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,10); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(1); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,15); + + row=table.AddRow(2); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,5); + row->UpdateCell(3,20); + + row=table.AddRow(3); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,15); + row->UpdateCell(3,5); + row->UpdateCell(4,"col index 4"); + + row=table.AddRow(4); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,30); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(5); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + //row->UpdateCell(2,25); + row->UpdateCell(3,5); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(6); + RakAssert(row); + row->UpdateCell(0,"Kevin Jenkins"); + row->UpdateCell(1,sizeof(dummydata), (char*)&dummydata); + row->UpdateCell(2,35); + //row->UpdateCell(3,40); + //row->UpdateCell(4,"should be unique"); + + row=table.AddRow(7); + RakAssert(row); + row->UpdateCell(0,"Bob Jenkins"); + + row=table.AddRow(8); + RakAssert(row); + row->UpdateCell(0,"Zack Jenkins"); + + // Test multi-column sorting + DataStructures::Table::Row *rows[30]; + DataStructures::Table::SortQuery queries[4]; + queries[0].columnIndex=0; + queries[0].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[1].columnIndex=1; + queries[1].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[2].columnIndex=2; + queries[2].operation=DataStructures::Table::QS_INCREASING_ORDER; + queries[3].columnIndex=3; + queries[3].operation=DataStructures::Table::QS_DECREASING_ORDER; + table.SortTable(queries, 4, rows); + unsigned i; + char out[256]; + RAKNET_DEBUG_PRINTF("Sort: Ascending except for column index 3\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, rows[i]); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test query: + // Don't return column 3, and swap columns 0 and 2 + unsigned columnsToReturn[4]; + columnsToReturn[0]=2; + columnsToReturn[1]=1; + columnsToReturn[2]=0; + columnsToReturn[3]=4; + DataStructures::Table resultsTable; + table.QueryTable(columnsToReturn,4,0,0,&resultsTable); + RAKNET_DEBUG_PRINTF("Query: Don't return column 3, and swap columns 0 and 2:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Test filter: + // Only return rows with column index 4 empty + DataStructures::Table::FilterQuery inclusionFilters[3]; + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + // inclusionFilters[0].cellValue; // Unused for IS_EMPTY + table.QueryTable(0,0,inclusionFilters,1,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Only return rows with column index 4 empty:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + // Column 5 empty and column 0 == Kevin Jenkins + inclusionFilters[0].columnIndex=4; + inclusionFilters[0].operation=DataStructures::Table::QF_IS_EMPTY; + inclusionFilters[1].columnIndex=0; + inclusionFilters[1].operation=DataStructures::Table::QF_EQUAL; + inclusionFilters[1].cellValue.Set("Kevin Jenkins"); + table.QueryTable(0,0,inclusionFilters,2,&resultsTable); + RAKNET_DEBUG_PRINTF("Filter: Column 5 empty and column 0 == Kevin Jenkins:\n"); + for (i=0; i < resultsTable.GetRowCount(); i++) + { + resultsTable.PrintRow(out,256,',',true, resultsTable.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + + RakNet::BitStream bs; + RAKNET_DEBUG_PRINTF("PreSerialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + StringCompressor::AddReference(); + TableSerializer::Serialize(&table, &bs); + TableSerializer::Deserialize(&bs, &table); + StringCompressor::RemoveReference(); + RAKNET_DEBUG_PRINTF("PostDeserialize:\n"); + for (i=0; i < table.GetRowCount(); i++) + { + table.PrintRow(out,256,',',true, table.GetRowByIndex(i)); + RAKNET_DEBUG_PRINTF("%s\n", out); + } + int a=5; +} +*/ diff --git a/Multiplayer/raknet/TelnetTransport.h b/Multiplayer/raknet/TelnetTransport.h new file mode 100644 index 000000000..8246f3c44 --- /dev/null +++ b/Multiplayer/raknet/TelnetTransport.h @@ -0,0 +1,67 @@ +/// \file +/// \brief Contains TelnetTransport , used to supports the telnet transport protocol. Insecure +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __TELNET_TRANSPORT +#define __TELNET_TRANSPORT + +#include "TransportInterface.h" +#include "DS_List.h" +#include "Export.h" +class TCPInterface; +struct TelnetClient; + +/// \brief Use TelnetTransport to easily allow windows telnet to connect to your ConsoleServer +/// To run Windows telnet, go to your start menu, click run, and in the edit box type "telnet " where is the ip address +/// of your ConsoleServer (most likely the same IP as your game). +/// This implementation always echos commands. +class RAK_DLL_EXPORT TelnetTransport : public TransportInterface +{ +public: + TelnetTransport(); + virtual ~TelnetTransport(); + bool Start(unsigned short port, bool serverMode); + void Stop(void); + void Send( SystemAddress systemAddress, const char *data, ... ); + void CloseConnection( SystemAddress systemAddress ); + Packet* Receive( void ); + void DeallocatePacket( Packet *packet ); + SystemAddress HasNewConnection(void); + SystemAddress HasLostConnection(void); + CommandParserInterface* GetCommandParser(void); + void SetSendSuffix(const char *suffix); + void SetSendPrefix(const char *prefix); +protected: + + struct TelnetClient + { + SystemAddress systemAddress; + char textInput[REMOTE_MAX_TEXT_INPUT]; + unsigned cursorPosition; + }; + + TCPInterface *tcpInterface; + void AutoAllocate(void); + bool ReassembleLine(TelnetTransport::TelnetClient* telnetClient, unsigned char c); + + // Crap this sucks but because windows telnet won't send line at a time, I have to reconstruct the lines at the server per player + DataStructures::List remoteClients; + + char *sendSuffix, *sendPrefix; + +}; + +#endif diff --git a/Multiplayer/raknet/ThreadPool.h b/Multiplayer/raknet/ThreadPool.h new file mode 100644 index 000000000..9d565f049 --- /dev/null +++ b/Multiplayer/raknet/ThreadPool.h @@ -0,0 +1,586 @@ +#ifndef __THREAD_POOL_H +#define __THREAD_POOL_H + +#include "RakMemoryOverride.h" +#include "DS_Queue.h" +#include "SimpleMutex.h" +#include "Export.h" +#include "RakThread.h" + +#ifdef _MSC_VER +#pragma warning( push ) +#endif + +class ThreadDataInterface +{ +public: + virtual void* PerThreadFactory(void *context)=0; + virtual void PerThreadDestructor(void* factoryResult, void *context)=0; +}; + +/// A simple class to create worker threads that processes a queue of functions with data. +/// This class does not allocate or deallocate memory. It is up to the user to handle memory management. +/// InputType and OutputType are stored directly in a queue. For large structures, if you plan to delete from the middle of the queue, +/// you might wish to store pointers rather than the structures themselves so the array can shift efficiently. +template +struct RAK_DLL_EXPORT ThreadPool +{ + ThreadPool(); + ~ThreadPool(); + + /// Start the specified number of threads. + /// \param[in] numThreads The number of threads to start + /// \param[in] stackSize 0 for default (except on consoles). + /// \param[in] _perThreadDataFactory User callback to return data stored per thread. Pass 0 if not needed. + /// \param[in] _perThreadDataDestructor User callback to destroy data stored per thread, created by _perThreadDataFactory. Pass 0 if not needed. + /// \return True on success, false on failure. + bool StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)()=0, void (*_perThreadDataDestructor)(void*)=0); + + // Alternate form of _perThreadDataFactory, _perThreadDataDestructor + void SetThreadDataInterface(ThreadDataInterface *tdi, void *context); + + /// Stops all threads + void StopThreads(void); + + /// Adds a function to a queue with data to pass to that function. This function will be called from the thread + /// Memory management is your responsibility! This class does not allocate or deallocate memory. + /// The best way to deallocate \a inputData is in userCallback. If you call EndThreads such that callbacks were not called, you + /// can iterate through the inputQueue and deallocate all pending input data there + /// The best way to deallocate output is as it is returned to you from GetOutput. Similarly, if you end the threads such that + /// not all output was returned, you can iterate through outputQueue and deallocate it there. + /// \param[in] workerThreadCallback The function to call from the thread + /// \param[in] inputData The parameter to pass to \a userCallback + void AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData); + + /// Adds to the output queue + /// Use it if you want to inject output into the same queue that the system uses. Normally you would not use this. Consider it a convenience function. + /// \param[in] outputData The output to inject + void AddOutput(OutputType outputData); + + /// Returns true if output from GetOutput is waiting. + /// \return true if output is waiting, false otherwise + bool HasOutput(void); + + /// Inaccurate but fast version of HasOutput. If this returns true, you should still check HasOutput for the real value. + /// \return true if output is probably waiting, false otherwise + bool HasOutputFast(void); + + /// Returns true if input from GetInput is waiting. + /// \return true if input is waiting, false otherwise + bool HasInput(void); + + /// Inaccurate but fast version of HasInput. If this returns true, you should still check HasInput for the real value. + /// \return true if input is probably waiting, false otherwise + bool HasInputFast(void); + + /// Gets the output of a call to \a userCallback + /// HasOutput must return true before you call this function. Otherwise it will assert. + /// \return The output of \a userCallback. If you have different output signatures, it is up to you to encode the data to indicate this + OutputType GetOutput(void); + + /// Clears internal buffers + void Clear(void); + + /// Lock the input buffer before calling the functions InputSize, InputAtIndex, and RemoveInputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockInput(void); + + /// Unlock the input buffer after you are done with the functions InputSize, GetInputAtIndex, and RemoveInputAtIndex + void UnlockInput(void); + + /// Length of the input queue + unsigned InputSize(void); + + /// Get the input at a specified index + InputType GetInputAtIndex(unsigned index); + + /// Remove input from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveInputAtIndex(unsigned index); + + /// Lock the output buffer before calling the functions OutputSize, OutputAtIndex, and RemoveOutputAtIndex + /// It is only necessary to lock the input or output while the threads are running + void LockOutput(void); + + /// Unlock the output buffer after you are done with the functions OutputSize, GetOutputAtIndex, and RemoveOutputAtIndex + void UnlockOutput(void); + + /// Length of the output queue + unsigned OutputSize(void); + + /// Get the output at a specified index + OutputType GetOutputAtIndex(unsigned index); + + /// Remove output from a specific index. This does NOT do memory deallocation - it only removes the item from the queue + void RemoveOutputAtIndex(unsigned index); + + /// Removes all items from the input queue + void ClearInput(void); + + /// Removes all items from the output queue + void ClearOutput(void); + + /// Are any of the threads working, or is input or output available? + bool IsWorking(void); + + /// The number of currently active threads. + int NumThreadsWorking(void); + + /// Have the threads been signaled to be stopped? + bool WasStopped(void) const; + +protected: + // It is valid to cancel input before it is processed. To do so, lock the inputQueue with inputQueueMutex, + // Scan the list, and remove the item you don't want. + SimpleMutex inputQueueMutex, outputQueueMutex, workingThreadCountMutex, runThreadsMutex; + + void* (*perThreadDataFactory)(); + void (*perThreadDataDestructor)(void*); + + // inputFunctionQueue & inputQueue are paired arrays so if you delete from one at a particular index you must delete from the other + // at the same index + DataStructures::Queue inputFunctionQueue; + DataStructures::Queue inputQueue; + DataStructures::Queue outputQueue; + + ThreadDataInterface *threadDataInterface; + void *tdiContext; + + + template + friend RAK_THREAD_DECLARATION(WorkerThread); + + /* +#ifdef _WIN32 + friend unsigned __stdcall WorkerThread( LPVOID arguments ); +#else + friend void* WorkerThread( void* arguments ); +#endif + */ + + /// \internal + bool runThreads; + /// \internal + int numThreadsRunning; + /// \internal + int numThreadsWorking; + /// \internal + SimpleMutex numThreadsRunningMutex; +#ifdef _WIN32 + /// \internal + HANDLE quitAndIncomingDataEvents[2]; +#endif +}; + +#include "ThreadPool.h" +#include "RakSleep.h" +#ifdef _WIN32 +#else +#include +#endif + +#ifdef _MSC_VER +#pragma warning(disable:4127) +#pragma warning( disable : 4701 ) // potentially uninitialized local variable 'inputData' used +#endif + +template +RAK_THREAD_DECLARATION(WorkerThread) +/* +#ifdef _WIN32 +unsigned __stdcall WorkerThread( LPVOID arguments ) +#else +void* WorkerThread( void* arguments ) +#endif +*/ +{ + bool returnOutput; + ThreadPool *threadPool = (ThreadPool*) arguments; + ThreadOutputType (*userCallback)(ThreadInputType, bool *, void*); + ThreadInputType inputData; + ThreadOutputType callbackOutput; + + userCallback=0; + + void *perThreadData; + if (threadPool->perThreadDataFactory) + perThreadData=threadPool->perThreadDataFactory(); + else if (threadPool->threadDataInterface) + perThreadData=threadPool->threadDataInterface->PerThreadFactory(threadPool->tdiContext); + else + perThreadData=0; + + // Increase numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + ++threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + while (1) + { +#ifdef _WIN32 + if (userCallback==0) + { + // Wait for signaled event + WaitForMultipleObjects( + 2, + threadPool->quitAndIncomingDataEvents, + false, + INFINITE); + } +#endif + + threadPool->runThreadsMutex.Lock(); + if (threadPool->runThreads==false) + { + threadPool->runThreadsMutex.Unlock(); + break; + } + threadPool->runThreadsMutex.Unlock(); + + threadPool->workingThreadCountMutex.Lock(); + ++threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + + // Read input data + userCallback=0; + threadPool->inputQueueMutex.Lock(); + if (threadPool->inputFunctionQueue.Size()) + { + userCallback=threadPool->inputFunctionQueue.Pop(); + inputData=threadPool->inputQueue.Pop(); + } + threadPool->inputQueueMutex.Unlock(); + + if (userCallback) + { + callbackOutput=userCallback(inputData, &returnOutput,perThreadData); + if (returnOutput) + { + threadPool->outputQueueMutex.Lock(); + threadPool->outputQueue.Push(callbackOutput); + threadPool->outputQueueMutex.Unlock(); + } + } + + threadPool->workingThreadCountMutex.Lock(); + --threadPool->numThreadsWorking; + threadPool->workingThreadCountMutex.Unlock(); + +#ifndef _WIN32 + // If no input data available, and GCC, then sleep. + if (userCallback==0) + RakSleep(1000); +#endif + } + + // Decrease numThreadsRunning + threadPool->numThreadsRunningMutex.Lock(); + --threadPool->numThreadsRunning; + threadPool->numThreadsRunningMutex.Unlock(); + + if (threadPool->perThreadDataDestructor) + threadPool->perThreadDataDestructor(perThreadData); + else if (threadPool->threadDataInterface) + threadPool->threadDataInterface->PerThreadDestructor(perThreadData, threadPool->tdiContext); + + return 0; +} +template +ThreadPool::ThreadPool() +{ + runThreads=false; + numThreadsRunning=0; + threadDataInterface=0; + tdiContext=0; + +} +template +ThreadPool::~ThreadPool() +{ + StopThreads(); + Clear(); +} +template +bool ThreadPool::StartThreads(int numThreads, int stackSize, void* (*_perThreadDataFactory)(), void (*_perThreadDataDestructor)(void *)) +{ + (void) stackSize; + + runThreadsMutex.Lock(); + if (runThreads==true) + { + runThreadsMutex.Unlock(); + return false; + } + runThreadsMutex.Unlock(); + +#ifdef _WIN32 + quitAndIncomingDataEvents[0]=CreateEvent(0, true, false, 0); + quitAndIncomingDataEvents[1]=CreateEvent(0, false, false, 0); +#endif + + perThreadDataFactory=_perThreadDataFactory; + perThreadDataDestructor=_perThreadDataDestructor; + + runThreadsMutex.Lock(); + runThreads=true; + runThreadsMutex.Unlock(); + + numThreadsWorking=0; + unsigned threadId = 0; + (void) threadId; + int i; + for (i=0; i < numThreads; i++) + { + int errorCode = RakNet::RakThread::Create(WorkerThread, this); + if (errorCode!=0) + { + StopThreads(); + return false; + } + } + // Wait for number of threads running to increase to numThreads + bool done=false; + while (done==false) + { + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==numThreads) + done=true; + numThreadsRunningMutex.Unlock(); + } + + return true; +} +template +void ThreadPool::SetThreadDataInterface(ThreadDataInterface *tdi, void *context) +{ + threadDataInterface=tdi; + tdiContext=context; +} +template +void ThreadPool::StopThreads(void) +{ + runThreadsMutex.Lock(); + if (runThreads==false) + { + runThreadsMutex.Unlock(); + return; + } + + runThreads=false; + runThreadsMutex.Unlock(); + +#ifdef _WIN32 + // Quit event + SetEvent(quitAndIncomingDataEvents[0]); +#endif + + // Wait for number of threads running to decrease to 0 + bool done=false; + while (done==false) + { + RakSleep(50); + numThreadsRunningMutex.Lock(); + if (numThreadsRunning==0) + done=true; + numThreadsRunningMutex.Unlock(); + } + +#ifdef _WIN32 + CloseHandle(quitAndIncomingDataEvents[0]); + CloseHandle(quitAndIncomingDataEvents[1]); +#endif +} +template +void ThreadPool::AddInput(OutputType (*workerThreadCallback)(InputType, bool *returnOutput, void* perThreadData), InputType inputData) +{ + inputQueueMutex.Lock(); + inputQueue.Push(inputData); + inputFunctionQueue.Push(workerThreadCallback); + inputQueueMutex.Unlock(); + +#ifdef _WIN32 + // Input data event + SetEvent(quitAndIncomingDataEvents[1]); +#endif +} +template +void ThreadPool::AddOutput(OutputType outputData) +{ + outputQueueMutex.Lock(); + outputQueue.Push(outputData); + outputQueueMutex.Unlock(); +} +template +bool ThreadPool::HasOutputFast(void) +{ + return outputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasOutput(void) +{ + bool res; + outputQueueMutex.Lock(); + res=outputQueue.IsEmpty()==false; + outputQueueMutex.Unlock(); + return res; +} +template +bool ThreadPool::HasInputFast(void) +{ + return inputQueue.IsEmpty()==false; +} +template +bool ThreadPool::HasInput(void) +{ + bool res; + inputQueueMutex.Lock(); + res=inputQueue.IsEmpty()==false; + inputQueueMutex.Unlock(); + return res; +} +template +OutputType ThreadPool::GetOutput(void) +{ + // Real output check + OutputType output; + outputQueueMutex.Lock(); + output=outputQueue.Pop(); + outputQueueMutex.Unlock(); + return output; +} +template +void ThreadPool::Clear(void) +{ + runThreadsMutex.Lock(); + if (runThreads) + { + runThreadsMutex.Unlock(); + inputQueueMutex.Lock(); + inputFunctionQueue.Clear(); + inputQueue.Clear(); + inputQueueMutex.Unlock(); + + outputQueueMutex.Lock(); + outputQueue.Clear(); + outputQueueMutex.Unlock(); + } + else + { + inputFunctionQueue.Clear(); + inputQueue.Clear(); + outputQueue.Clear(); + } +} +template +void ThreadPool::LockInput(void) +{ + inputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockInput(void) +{ + inputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::InputSize(void) +{ + return inputQueue.Size(); +} +template +InputType ThreadPool::GetInputAtIndex(unsigned index) +{ + return inputQueue[index]; +} +template +void ThreadPool::RemoveInputAtIndex(unsigned index) +{ + inputQueue.RemoveAtIndex(index); + inputFunctionQueue.RemoveAtIndex(index); +} +template +void ThreadPool::LockOutput(void) +{ + outputQueueMutex.Lock(); +} +template +void ThreadPool::UnlockOutput(void) +{ + outputQueueMutex.Unlock(); +} +template +unsigned ThreadPool::OutputSize(void) +{ + return outputQueue.Size(); +} +template +OutputType ThreadPool::GetOutputAtIndex(unsigned index) +{ + return outputQueue[index]; +} +template +void ThreadPool::RemoveOutputAtIndex(unsigned index) +{ + outputQueue.RemoveAtIndex(index); +} +template +void ThreadPool::ClearInput(void) +{ + inputQueue.Clear(); + inputFunctionQueue.Clear(); +} + +template +void ThreadPool::ClearOutput(void) +{ + outputQueue.Clear(); +} +template +bool ThreadPool::IsWorking(void) +{ + bool isWorking; +// workingThreadCountMutex.Lock(); +// isWorking=numThreadsWorking!=0; +// workingThreadCountMutex.Unlock(); + +// if (isWorking) +// return true; + + // Bug fix: Originally the order of these two was reversed. + // It's possible with the thread timing that working could have been false, then it picks up the data in the other thread, then it checks + // here and sees there is no data. So it thinks the thread is not working when it was. + if (HasOutputFast() && HasOutput()) + return true; + + if (HasInputFast() && HasInput()) + return true; + + // Need to check is working again, in case the thread was between the first and second checks + workingThreadCountMutex.Lock(); + isWorking=numThreadsWorking!=0; + workingThreadCountMutex.Unlock(); + + return isWorking; +} + +template +int ThreadPool::NumThreadsWorking(void) +{ + return numThreadsWorking; +} + +template +bool ThreadPool::WasStopped(void) const +{ + bool b; + runThreadsMutex.Lock(); + b = runThreads; + runThreadsMutex.Unlock(); + return b; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/Multiplayer/raknet/ThreadsafePacketLogger.h b/Multiplayer/raknet/ThreadsafePacketLogger.h new file mode 100644 index 000000000..5c625a664 --- /dev/null +++ b/Multiplayer/raknet/ThreadsafePacketLogger.h @@ -0,0 +1,40 @@ +/// \file +/// \brief Derivation of the packet logger to defer the call to WriteLog until the user thread. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __THREADSAFE_PACKET_LOGGER_H +#define __THREADSAFE_PACKET_LOGGER_H + +#include "PacketLogger.h" +#include "SingleProducerConsumer.h" + +/// \ingroup PACKETLOGGER_GROUP +/// \brief Same as PacketLogger, but writes output in the user thread. +class RAK_DLL_EXPORT ThreadsafePacketLogger : public PacketLogger +{ +public: + ThreadsafePacketLogger(); + virtual ~ThreadsafePacketLogger(); + + virtual void Update(RakPeerInterface *peer); + +protected: + virtual void AddToLog(const char *str); + + DataStructures::SingleProducerConsumer logMessages; +}; + +#endif diff --git a/Multiplayer/raknet/TransportInterface.h b/Multiplayer/raknet/TransportInterface.h new file mode 100644 index 000000000..1561d0d42 --- /dev/null +++ b/Multiplayer/raknet/TransportInterface.h @@ -0,0 +1,86 @@ +/// \file +/// \brief Contains TransportInterface from which you can derive custom transport providers for ConsoleServer. +/// +/// This file is part of RakNet Copyright 2003 Kevin Jenkins. +/// +/// Usage of RakNet is subject to the appropriate license agreement. +/// Creative Commons Licensees are subject to the +/// license found at +/// http://creativecommons.org/licenses/by-nc/2.5/ +/// Single application licensees are subject to the license found at +/// http://www.jenkinssoftware.com/SingleApplicationLicense.html +/// Custom license users are subject to the terms therein. +/// GPL license users are subject to the GNU General Public +/// License as published by the Free +/// Software Foundation; either version 2 of the License, or (at your +/// option) any later version. + +#ifndef __TRANSPORT_INTERFACE_H +#define __TRANSPORT_INTERFACE_H + +#include "RakNetTypes.h" +#include "Export.h" +#include "RakMemoryOverride.h" + +#define REMOTE_MAX_TEXT_INPUT 2048 + +class CommandParserInterface; + +/// \brief Defines an interface that is used to send and receive null-terminated strings. +/// In practice this is only used by the CommandParser system for for servers. +class RAK_DLL_EXPORT TransportInterface +{ +public: + TransportInterface() {} + virtual ~TransportInterface() {} + + /// Start the transport provider on the indicated port. + /// \param[in] port The port to start the transport provider on + /// \param[in] serverMode If true, you should allow incoming connections (I don't actually use this anywhere) + /// \return Return true on success, false on failure. + virtual bool Start(unsigned short port, bool serverMode)=0; + + /// Stop the transport provider. You can clear memory and shutdown threads here. + virtual void Stop(void)=0; + + /// Send a null-terminated string to \a systemAddress + /// If your transport method requires particular formatting of the outgoing data (e.g. you don't just send strings) you can do it here + /// and parse it out in Receive(). + /// \param[in] systemAddress The player to send the string to + /// \param[in] data format specifier - same as RAKNET_DEBUG_PRINTF + /// \param[in] ... format specification arguments - same as RAKNET_DEBUG_PRINTF + virtual void Send( SystemAddress systemAddress, const char *data, ... )=0; + + /// Disconnect \a systemAddress . The binary address and port defines the SystemAddress structure. + /// \param[in] systemAddress The player/address to disconnect + virtual void CloseConnection( SystemAddress systemAddress )=0; + + /// Return a string. The string should be allocated and written to Packet::data . + /// The byte length should be written to Packet::length . The player/address should be written to Packet::systemAddress + /// If your transport protocol adds special formatting to the data stream you should parse it out before returning it in the packet + /// and thus only return a string in Packet::data + /// \return The packet structure containing the result of Receive, or 0 if no data is available + virtual Packet* Receive( void )=0; + + /// Deallocate the Packet structure returned by Receive + /// \param[in] The packet to deallocate + virtual void DeallocatePacket( Packet *packet )=0; + + /// If a new system connects to you, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasNewConnection(void)=0; + + /// If a system loses the connection, you should queue that event and return the systemAddress/address of that player in this function. + /// \return The SystemAddress/address of the system + virtual SystemAddress HasLostConnection(void)=0; + + /// Your transport provider can itself have command parsers if the transport layer has user-modifiable features + /// For example, your transport layer may have a password which you want remote users to be able to set or you may want + /// to allow remote users to turn on or off command echo + /// \return 0 if you do not need a command parser - otherwise the desired derivation of CommandParserInterface + virtual CommandParserInterface* GetCommandParser(void)=0; +protected: +}; + +#endif + diff --git a/Multiplayer/raknet/WSAStartupSingleton.h b/Multiplayer/raknet/WSAStartupSingleton.h new file mode 100644 index 000000000..d2930fe16 --- /dev/null +++ b/Multiplayer/raknet/WSAStartupSingleton.h @@ -0,0 +1,16 @@ +#ifndef __WSA_STARTUP_SINGLETON_H +#define __WSA_STARTUP_SINGLETON_H + +class WSAStartupSingleton +{ +public: + WSAStartupSingleton(); + ~WSAStartupSingleton(); + static void AddRef(void); + static void Deref(void); + +protected: + static int refCount; +}; + +#endif diff --git a/Multiplayer/raknet/_FindFirst.h b/Multiplayer/raknet/_FindFirst.h new file mode 100644 index 000000000..62a878375 --- /dev/null +++ b/Multiplayer/raknet/_FindFirst.h @@ -0,0 +1,54 @@ +/** + * Original file by the_viking, fixed by RĂ´mulo Fernandes + * Should emulate windows finddata structure + */ + +#ifndef GCC_FINDFIRST_H +#define GCC_FINDFIRST_H + +#if (defined(__GNUC__) || defined(__GCCXML__)) && !defined(__WIN32) + +#include + +#include "RakString.h" + +#define _A_NORMAL 0x00 // Normal file +#define _A_RDONLY 0x01 // Read-only file +#define _A_HIDDEN 0x02 // Hidden file +#define _A_SYSTEM 0x04 // System file +#define _A_VOLID 0x08 // Volume ID +#define _A_SUBDIR 0x10 // Subdirectory +#define _A_ARCH 0x20 // File changed since last archive +#define FA_NORMAL 0x00 // Synonym of _A_NORMAL +#define FA_RDONLY 0x01 // Synonym of _A_RDONLY +#define FA_HIDDEN 0x02 // Synonym of _A_HIDDEN +#define FA_SYSTEM 0x04 // Synonym of _A_SYSTEM +#define FA_LABEL 0x08 // Synonym of _A_VOLID +#define FA_DIREC 0x10 // Synonym of _A_SUBDIR +#define FA_ARCH 0x20 // Synonym of _A_ARCH + + +const unsigned STRING_BUFFER_SIZE = 512; + +typedef struct _finddata_t +{ + char name[STRING_BUFFER_SIZE]; + int attrib; + unsigned long size; +} _finddata; + +/** Hold information about the current search +*/ +typedef struct _findinfo_t +{ + DIR* openedDir; + RakNet::RakString filter; + RakNet::RakString dirName; +} _findinfo; + +long _findfirst(const char *name, _finddata_t *f); +int _findnext(long h, _finddata_t *f); +int _findclose(long h); + +#endif +#endif diff --git a/Multiplayer/raknet/raknetlibstatic.pdb b/Multiplayer/raknet/raknetlibstatic.pdb deleted file mode 100644 index 84b070c06..000000000 Binary files a/Multiplayer/raknet/raknetlibstatic.pdb and /dev/null differ diff --git a/Multiplayer/raknet/raknetlibstaticdebug.pdb b/Multiplayer/raknet/raknetlibstaticdebug.pdb index 89141d9d4..f9e4ef6d6 100644 Binary files a/Multiplayer/raknet/raknetlibstaticdebug.pdb and b/Multiplayer/raknet/raknetlibstaticdebug.pdb differ diff --git a/TestCB.cpp b/TestCB.cpp new file mode 100644 index 000000000..e2e34477b --- /dev/null +++ b/TestCB.cpp @@ -0,0 +1,143 @@ +#include "RakPeerInterface.h" +#include "FileListTransfer.h" +#include "RakSleep.h" +#include "RakNetworkFactory.h" +#include "MessageIdentifiers.h" +#include "FileListTransferCBInterface.h" +#include "FileOperations.h" +#include "SuperFastHash.h" +#include "RakAssert.h" +#include "BitStream.h" +#include "IncrementalReadInterface.h" +#include + +RakNet::RakString file; +RakNet::RakString fileCopy; + +//const char *file = "c:/temp/unittest.vcproj"; +//const char *fileCopy = "c:/temp/unittest_copy.vcproj"; + +class TestCB : public FileListTransferCBInterface +{ +public: + bool OnFile( + OnFileStruct *onFileStruct) + { + printf("%i. (100%%) %i/%i %s %ib->%ib / %ib->%ib\n", onFileStruct->setID, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength); + + + FILE *fp = fopen(fileCopy.C_String(), "wb"); + fwrite(onFileStruct->fileData, onFileStruct->finalDataLength, 1, fp); + fclose(fp); + + // Make sure it worked + unsigned int hash1 = SuperFastHashFile(file.C_String()); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash1, sizeof(hash1)); + unsigned int hash2 = SuperFastHashFile(fileCopy.C_String()); + if (RakNet::BitStream::DoEndianSwap()) + RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash2, sizeof(hash2)); + RakAssert(hash1==hash2); + + // Return true to have RakNet delete the memory allocated to hold this file. + // False if you hold onto the memory, and plan to delete it yourself later + return true; + } + + virtual void OnFileProgress(OnFileStruct *onFileStruct,unsigned int partCount,unsigned int partTotal,unsigned int partLength, char *firstDataChunk) + { + printf("%i (%i%%) %i/%i %s %ib->%ib / %ib->%ib\n", onFileStruct->setID, 100*partCount/partTotal, onFileStruct->fileIndex+1, onFileStruct->setCount, onFileStruct->fileName, onFileStruct->compressedTransmissionLength, onFileStruct->finalDataLength, onFileStruct->setTotalCompressedTransmissionLength, onFileStruct->setTotalFinalLength, firstDataChunk); + } + + virtual bool OnDownloadComplete(void) + { + printf("Download complete.\n"); + + // Returning false automatically deallocates the automatically allocated handler that was created by DirectoryDeltaTransfer + return false; + } + +} transferCallback; + +// Sender progress notification +class TestFileListProgress : public FileListProgress +{ + virtual void OnFilePush(const char *fileName, unsigned int fileLengthBytes, unsigned int offset, unsigned int bytesBeingSent, bool done, SystemAddress targetSystem) + { + printf("Sending %s bytes=%i offset=%i\n", fileName, bytesBeingSent, offset); + } +} testFileListProgress; + + +void TestFileTransfer(void) +{ + printf("This sample demonstrates incrementally sending a file with\n"); + printf("the FileListTransferPlugin. Incremental sends will read and send the.\n"); + printf("file only as needed, rather than putting the whole file in memory.\n"); + printf("This is to support sending large files to many users at the same time.\n"); + printf("Difficulty: Intermediate\n\n"); + + TestCB testCB; + RakPeerInterface *peer1 = RakNetworkFactory::GetRakPeerInterface(); + RakPeerInterface *peer2 = RakNetworkFactory::GetRakPeerInterface(); + peer1->Startup(1,0,&SocketDescriptor(60000,0),1); + peer2->Startup(1,0,&SocketDescriptor(60001,0),1); + peer1->SetMaximumIncomingConnections(1); + peer2->Connect("127.0.0.1",60000,0,0,0); + FileListTransfer flt1, flt2; + peer1->AttachPlugin(&flt1); + peer2->AttachPlugin(&flt2); + peer1->SetSplitMessageProgressInterval(1); + peer2->SetSplitMessageProgressInterval(1); + // Print sending progress notifications + flt1.SetCallback(&testFileListProgress); + FileList fileList; + IncrementalReadInterface incrementalReadInterface; + printf("Enter complete filename with path to test:\n"); + char str[256]; + gets(str); + file=str; + fileCopy=file+"_copy"; + // Reference this file, rather than add it in memory. Will send 1000 byte chunks. The reason to do this is so the whole file does not have to be in memory at once + unsigned int fileLength = GetFileLength(file.C_String()); + if (fileLength==0) + { + printf("Test file %s not found.\n", file.C_String()); + RakNetworkFactory::DestroyRakPeerInterface(peer1); + RakNetworkFactory::DestroyRakPeerInterface(peer1); + return; + } + fileList.AddFile(file.C_String(), 0, fileLength, fileLength, FileListNodeContext(0,0), true); + // Wait for the connection + RakSleep(100); + Packet *packet1, *packet2; + while (1) + { + // Wait for the connection request to be accepted + packet2=peer2->Receive(); + if (packet2->data[0]==ID_CONNECTION_REQUEST_ACCEPTED) + { + flt2.SetupReceive(&testCB, false, packet2->systemAddress); + peer2->DeallocatePacket(packet2); + break; + } + peer2->DeallocatePacket(packet2); + RakSleep(30); + } + + // When connected, send the file. Since the file is a reference, it will be sent incrementally + while (1) + { + packet1=peer1->Receive(); + packet2=peer2->Receive(); + if (packet1 && packet1->data[0]==ID_NEW_INCOMING_CONNECTION) + flt1.Send(&fileList,peer1,packet1->systemAddress,0,HIGH_PRIORITY,0,false, &incrementalReadInterface, 5000); + peer1->DeallocatePacket(packet1); + peer2->DeallocatePacket(packet2); + RakSleep(30); + } + + + RakNetworkFactory::DestroyRakPeerInterface(peer1); + RakNetworkFactory::DestroyRakPeerInterface(peer1); +} \ No newline at end of file diff --git a/ja2_2005Express.vcproj b/ja2_2005Express.vcproj index 68aa9519a..41ced8be4 100644 --- a/ja2_2005Express.vcproj +++ b/ja2_2005Express.vcproj @@ -17,7 +17,7 @@ + +