forked from aya/aya
Initial commit
This commit is contained in:
230
third-party/RakNet/CMakeLists.txt
vendored
Normal file
230
third-party/RakNet/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
add_library(RakNet STATIC
|
||||
src/RakNet/AutopatcherPatchContext.hpp
|
||||
src/RakNet/AutopatcherRepositoryInterface.hpp
|
||||
src/RakNet/BitStream.cpp
|
||||
src/RakNet/BitStream.hpp
|
||||
src/RakNet/CCRakNetSlidingWindow.cpp
|
||||
src/RakNet/CCRakNetSlidingWindow.hpp
|
||||
src/RakNet/CCRakNetUDT.cpp
|
||||
src/RakNet/CCRakNetUDT.hpp
|
||||
src/RakNet/CheckSum.cpp
|
||||
src/RakNet/CheckSum.hpp
|
||||
src/RakNet/CommandParserInterface.cpp
|
||||
src/RakNet/CommandParserInterface.hpp
|
||||
src/RakNet/ConnectionGraph2.cpp
|
||||
src/RakNet/ConnectionGraph2.hpp
|
||||
src/RakNet/ConsoleServer.hpp
|
||||
src/RakNet/DataCompressor.cpp
|
||||
src/RakNet/DataCompressor.hpp
|
||||
src/RakNet/DirectoryDeltaTransfer.cpp
|
||||
src/RakNet/DirectoryDeltaTransfer.hpp
|
||||
src/RakNet/DS_BinarySearchTree.hpp
|
||||
src/RakNet/DS_BPlusTree.hpp
|
||||
src/RakNet/DS_BytePool.cpp
|
||||
src/RakNet/DS_BytePool.hpp
|
||||
src/RakNet/DS_ByteQueue.cpp
|
||||
src/RakNet/DS_ByteQueue.hpp
|
||||
src/RakNet/DS_Heap.hpp
|
||||
src/RakNet/DS_HuffmanEncodingTree.cpp
|
||||
src/RakNet/DS_HuffmanEncodingTree.hpp
|
||||
src/RakNet/DS_HuffmanEncodingTreeFactory.hpp
|
||||
src/RakNet/DS_HuffmanEncodingTreeNode.hpp
|
||||
src/RakNet/DS_LinkedList.hpp
|
||||
src/RakNet/DS_List.hpp
|
||||
src/RakNet/DS_Map.hpp
|
||||
src/RakNet/DS_MemoryPool.hpp
|
||||
src/RakNet/DS_Multilist.hpp
|
||||
src/RakNet/DS_OrderedChannelHeap.hpp
|
||||
src/RakNet/DS_OrderedList.hpp
|
||||
src/RakNet/DS_Queue.hpp
|
||||
src/RakNet/DS_QueueLinkedList.hpp
|
||||
src/RakNet/DS_RangeList.hpp
|
||||
src/RakNet/DS_Table.cpp
|
||||
src/RakNet/DS_Table.hpp
|
||||
src/RakNet/DS_ThreadsafeAllocatingQueue.hpp
|
||||
src/RakNet/DS_Tree.hpp
|
||||
src/RakNet/DS_WeightedGraph.hpp
|
||||
src/RakNet/EmailSender.cpp
|
||||
src/RakNet/EmailSender.hpp
|
||||
src/RakNet/EncodeClassName.cpp
|
||||
src/RakNet/EpochTimeToString.cpp
|
||||
src/RakNet/EpochTimeToString.hpp
|
||||
src/RakNet/Export.hpp
|
||||
src/RakNet/FileList.cpp
|
||||
src/RakNet/FileList.hpp
|
||||
src/RakNet/FileListNodeContext.hpp
|
||||
src/RakNet/FileListTransfer.cpp
|
||||
src/RakNet/FileListTransfer.hpp
|
||||
src/RakNet/FileListTransferCBInterface.hpp
|
||||
src/RakNet/FileOperations.cpp
|
||||
src/RakNet/FileOperations.hpp
|
||||
src/RakNet/FormatString.cpp
|
||||
src/RakNet/FormatString.hpp
|
||||
src/RakNet/FullyConnectedMesh2.cpp
|
||||
src/RakNet/FullyConnectedMesh2.hpp
|
||||
src/RakNet/Getche.cpp
|
||||
src/RakNet/Getche.hpp
|
||||
src/RakNet/GetTime.cpp
|
||||
src/RakNet/GetTime.hpp
|
||||
src/RakNet/gettimeofday.cpp
|
||||
src/RakNet/gettimeofday.hpp
|
||||
src/RakNet/GridSectorizer.cpp
|
||||
src/RakNet/GridSectorizer.hpp
|
||||
src/RakNet/HTTPConnection.cpp
|
||||
src/RakNet/HTTPConnection.hpp
|
||||
src/RakNet/IncrementalReadInterface.cpp
|
||||
src/RakNet/IncrementalReadInterface.hpp
|
||||
src/RakNet/InternalPacket.hpp
|
||||
src/RakNet/Itoa.cpp
|
||||
src/RakNet/Itoa.hpp
|
||||
src/RakNet/Kbhit.hpp
|
||||
src/RakNet/LinuxStrings.cpp
|
||||
src/RakNet/LinuxStrings.hpp
|
||||
src/RakNet/LocklessTypes.cpp
|
||||
src/RakNet/LocklessTypes.hpp
|
||||
src/RakNet/LogCommandParser.cpp
|
||||
src/RakNet/LogCommandParser.hpp
|
||||
src/RakNet/MessageFilter.cpp
|
||||
src/RakNet/MessageFilter.hpp
|
||||
src/RakNet/MessageIdentifiers.hpp
|
||||
src/RakNet/MTUSize.hpp
|
||||
src/RakNet/NativeFeatureIncludes.hpp
|
||||
src/RakNet/NativeFeatureIncludesOverrides.hpp
|
||||
src/RakNet/NativeTypes.hpp
|
||||
src/RakNet/NatPunchthroughClient.cpp
|
||||
src/RakNet/NatPunchthroughClient.hpp
|
||||
src/RakNet/NatPunchthroughServer.cpp
|
||||
src/RakNet/NatPunchthroughServer.hpp
|
||||
src/RakNet/NatTypeDetectionClient.cpp
|
||||
src/RakNet/NatTypeDetectionClient.hpp
|
||||
src/RakNet/NatTypeDetectionCommon.cpp
|
||||
src/RakNet/NatTypeDetectionCommon.hpp
|
||||
src/RakNet/NatTypeDetectionServer.cpp
|
||||
src/RakNet/NatTypeDetectionServer.hpp
|
||||
src/RakNet/NetworkIDManager.cpp
|
||||
src/RakNet/NetworkIDManager.hpp
|
||||
src/RakNet/NetworkIDObject.cpp
|
||||
src/RakNet/NetworkIDObject.hpp
|
||||
src/RakNet/PacketConsoleLogger.cpp
|
||||
src/RakNet/PacketConsoleLogger.hpp
|
||||
src/RakNet/PacketFileLogger.cpp
|
||||
src/RakNet/PacketFileLogger.hpp
|
||||
src/RakNet/PacketizedTCP.cpp
|
||||
src/RakNet/PacketizedTCP.hpp
|
||||
src/RakNet/PacketLogger.cpp
|
||||
src/RakNet/PacketLogger.hpp
|
||||
src/RakNet/PacketOutputWindowLogger.cpp
|
||||
src/RakNet/PacketOutputWindowLogger.hpp
|
||||
src/RakNet/PacketPool.hpp
|
||||
src/RakNet/PacketPriority.hpp
|
||||
src/RakNet/PluginInterface2.cpp
|
||||
src/RakNet/PluginInterface2.hpp
|
||||
src/RakNet/RakAlloca.hpp
|
||||
src/RakNet/RakAssert.hpp
|
||||
src/RakNet/RakMemoryOverride.cpp
|
||||
src/RakNet/RakMemoryOverride.hpp
|
||||
src/RakNet/RakNetCommandParser.cpp
|
||||
src/RakNet/RakNetCommandParser.hpp
|
||||
src/RakNet/RakNetDefines.hpp
|
||||
src/RakNet/RakNetDefinesOverrides.hpp
|
||||
src/RakNet/RakNetSmartPtr.hpp
|
||||
src/RakNet/RakNetSocket.cpp
|
||||
src/RakNet/RakNetSocket.hpp
|
||||
src/RakNet/RakNetStatistics.cpp
|
||||
src/RakNet/RakNetStatistics.hpp
|
||||
src/RakNet/RakNetTime.hpp
|
||||
src/RakNet/RakNetTransport2.cpp
|
||||
src/RakNet/RakNetTransport2.hpp
|
||||
src/RakNet/RakNetTypes.cpp
|
||||
src/RakNet/RakNetTypes.hpp
|
||||
src/RakNet/RakNetVersion.hpp
|
||||
src/RakNet/RakPeer.cpp
|
||||
src/RakNet/RakPeer.hpp
|
||||
src/RakNet/RakPeerInterface.hpp
|
||||
src/RakNet/RakSleep.cpp
|
||||
src/RakNet/RakSleep.hpp
|
||||
src/RakNet/RakString.cpp
|
||||
src/RakNet/RakString.hpp
|
||||
src/RakNet/RakThread.cpp
|
||||
src/RakNet/RakThread.hpp
|
||||
src/RakNet/Rand.cpp
|
||||
src/RakNet/Rand.hpp
|
||||
src/RakNet/rdlmalloc-options.hpp
|
||||
src/RakNet/rdlmalloc.cpp
|
||||
src/RakNet/rdlmalloc.hpp
|
||||
src/RakNet/ReadyEvent.cpp
|
||||
src/RakNet/ReadyEvent.hpp
|
||||
src/RakNet/RefCountedObj.hpp
|
||||
src/RakNet/ReliabilityLayer.cpp
|
||||
src/RakNet/ReliabilityLayer.hpp
|
||||
src/RakNet/ReplicaEnums.hpp
|
||||
src/RakNet/ReplicaManager3.cpp
|
||||
src/RakNet/ReplicaManager3.hpp
|
||||
src/RakNet/Router2.cpp
|
||||
src/RakNet/Router2.hpp
|
||||
src/RakNet/RPC4Plugin.cpp
|
||||
src/RakNet/RPC4Plugin.hpp
|
||||
src/RakNet/SendToThread.cpp
|
||||
src/RakNet/SendToThread.hpp
|
||||
src/RakNet/SHA1.cpp
|
||||
src/RakNet/SHA1.hpp
|
||||
src/RakNet/SignaledEvent.cpp
|
||||
src/RakNet/SignaledEvent.hpp
|
||||
src/RakNet/SimpleMutex.cpp
|
||||
src/RakNet/SimpleMutex.hpp
|
||||
src/RakNet/SimpleTCPServer.hpp
|
||||
src/RakNet/SingleProducerConsumer.hpp
|
||||
src/RakNet/SocketIncludes.hpp
|
||||
src/RakNet/SocketLayer.cpp
|
||||
src/RakNet/SocketLayer.hpp
|
||||
src/RakNet/SQLite3Plugin/sqlite3.hpp
|
||||
src/RakNet/SQLite3Plugin/SQLite3ClientPlugin.cpp
|
||||
src/RakNet/SQLite3Plugin/SQLite3ClientPlugin.hpp
|
||||
src/RakNet/SQLite3Plugin/sqlite3ext.hpp
|
||||
src/RakNet/SQLite3Plugin/SQLite3PluginCommon.cpp
|
||||
src/RakNet/SQLite3Plugin/SQLite3PluginCommon.hpp
|
||||
src/RakNet/SQLite3Plugin/SQLiteClientLoggerPlugin.cpp
|
||||
src/RakNet/SQLite3Plugin/SQLiteClientLoggerPlugin.hpp
|
||||
src/RakNet/SQLite3Plugin/SQLiteLoggerCommon.cpp
|
||||
src/RakNet/SQLite3Plugin/SQLiteLoggerCommon.hpp
|
||||
src/RakNet/StringCompressor.cpp
|
||||
src/RakNet/StringCompressor.hpp
|
||||
src/RakNet/StringTable.cpp
|
||||
src/RakNet/StringTable.hpp
|
||||
src/RakNet/SuperFastHash.cpp
|
||||
src/RakNet/SuperFastHash.hpp
|
||||
src/RakNet/TableSerializer.cpp
|
||||
src/RakNet/TableSerializer.hpp
|
||||
src/RakNet/TCPInterface.cpp
|
||||
src/RakNet/TCPInterface.hpp
|
||||
src/RakNet/TeamBalancer.cpp
|
||||
src/RakNet/TeamBalancer.hpp
|
||||
src/RakNet/TelnetTransport.cpp
|
||||
src/RakNet/TelnetTransport.hpp
|
||||
src/RakNet/ThreadPool.hpp
|
||||
src/RakNet/ThreadsafePacketLogger.cpp
|
||||
src/RakNet/ThreadsafePacketLogger.hpp
|
||||
src/RakNet/TransportInterface.hpp
|
||||
src/RakNet/UDPForwarder.cpp
|
||||
src/RakNet/UDPForwarder.hpp
|
||||
src/RakNet/UDPProxyClient.cpp
|
||||
src/RakNet/UDPProxyClient.hpp
|
||||
src/RakNet/UDPProxyCommon.hpp
|
||||
src/RakNet/UDPProxyCoordinator.cpp
|
||||
src/RakNet/UDPProxyCoordinator.hpp
|
||||
src/RakNet/UDPProxyServer.cpp
|
||||
src/RakNet/UDPProxyServer.hpp
|
||||
src/RakNet/VariableDeltaSerializer.cpp
|
||||
src/RakNet/VariableDeltaSerializer.hpp
|
||||
src/RakNet/VariableListDeltaTracker.cpp
|
||||
src/RakNet/VariableListDeltaTracker.hpp
|
||||
src/RakNet/VariadicSQLParser.cpp
|
||||
src/RakNet/VariadicSQLParser.hpp
|
||||
src/RakNet/WindowsIncludes.hpp
|
||||
src/RakNet/WSAStartupSingleton.cpp
|
||||
src/RakNet/WSAStartupSingleton.hpp
|
||||
src/RakNet/_FindFirst.cpp
|
||||
src/RakNet/_FindFirst.hpp
|
||||
)
|
||||
|
||||
target_include_directories(RakNet PUBLIC src PRIVATE ${ENGINE_DIR}/app/src ${ENGINE_DIR}/core/src)
|
||||
23
third-party/RakNet/src/RakNet/AutopatcherPatchContext.hpp
vendored
Normal file
23
third-party/RakNet/src/RakNet/AutopatcherPatchContext.hpp
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
///
|
||||
|
||||
#ifndef __AUTOPATCHER_PATCH_CONTEXT_H
|
||||
#define __AUTOPATCHER_PATCH_CONTEXT_H
|
||||
|
||||
enum PatchContext
|
||||
{
|
||||
PC_HASH_1_WITH_PATCH,
|
||||
PC_HASH_2_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
|
||||
72
third-party/RakNet/src/RakNet/AutopatcherRepositoryInterface.hpp
vendored
Normal file
72
third-party/RakNet/src/RakNet/AutopatcherRepositoryInterface.hpp
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
///
|
||||
/// \file AutopatcherRepositoryInterface.h
|
||||
/// \brief An interface used by AutopatcherServer to get the data necessary to run an autopatcher.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
///
|
||||
|
||||
#ifndef __AUTOPATCHER_REPOSITORY_INTERFACE_H
|
||||
#define __AUTOPATCHER_REPOSITORY_INTERFACE_H
|
||||
|
||||
#include "IncrementalReadInterface.hpp"
|
||||
#include "SimpleMutex.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class FileList;
|
||||
class BitStream;
|
||||
|
||||
/// An interface used by AutopatcherServer to get the data necessary to run an autopatcher. This is up to you to implement for custom repository solutions.
|
||||
class AutopatcherRepositoryInterface : public 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, double sinceDate)=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)=0;
|
||||
|
||||
/// For the most recent update, return files that were patched, added, or deleted. For files that were patched, return both the patch in \a patchedFiles and the current version in \a updatedFiles
|
||||
/// The cache will be used if the client last patched between \a priorRowPatchTime and \a mostRecentRowPatchTime
|
||||
/// No files changed will be returned to the client if the client last patched after mostRecentRowPatchTime
|
||||
/// \param[in,out] applicationName Name of the application to get patches for. If empty, uses the most recently updated application, and the string will be updated to reflect this name.
|
||||
/// \param[out] patchedFiles Given the most recent update, if a file was patched, add it to this list. The context data for each file will be PC_HASH_WITH_PATCH. The first 4 bytes of data should be a hash of the file being patched. The second 4 bytes should be the hash of the file after the patch. The remaining bytes should be the patch itself.
|
||||
/// \param[out] updatedFiles The current value of the file. List should have the same length and order as \a patchedFiles
|
||||
/// \param[out] updatedFileHashes The hash of the current value of the file. List should have the same length and order as \a patchedFiles
|
||||
/// \param[out] deletedFiles Files that were deleted in the last patch.
|
||||
/// \param[out] priorRowPatchTime When the patch before the most recent patch took place. 0 means never.
|
||||
/// \param[out] mostRecentRowPatchTime When the most recent patch took place. 0 means never.
|
||||
/// \return true on success, false on failure
|
||||
virtual bool GetMostRecentChangelistWithPatches(
|
||||
RakNet::RakString &applicationName,
|
||||
FileList *patchedFiles,
|
||||
FileList *updatedFiles,
|
||||
FileList *updatedFileHashes,
|
||||
FileList *deletedFiles,
|
||||
double *priorRowPatchTime,
|
||||
double *mostRecentRowPatchTime)=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;
|
||||
|
||||
/// \return Passed to FileListTransfer::Send() as the _chunkSize parameter.
|
||||
virtual const int GetIncrementalReadChunkSize(void) const=0;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
1233
third-party/RakNet/src/RakNet/BitStream.cpp
vendored
Normal file
1233
third-party/RakNet/src/RakNet/BitStream.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2037
third-party/RakNet/src/RakNet/BitStream.hpp
vendored
Normal file
2037
third-party/RakNet/src/RakNet/BitStream.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
332
third-party/RakNet/src/RakNet/CCRakNetSlidingWindow.cpp
vendored
Normal file
332
third-party/RakNet/src/RakNet/CCRakNetSlidingWindow.cpp
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
#include "CCRakNetSlidingWindow.hpp"
|
||||
|
||||
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1
|
||||
|
||||
static const double UNSET_TIME_US=-1;
|
||||
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
static const CCTimeType SYN=10;
|
||||
#else
|
||||
static const CCTimeType SYN=10000;
|
||||
#endif
|
||||
|
||||
#include "MTUSize.hpp"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include "RakAssert.hpp"
|
||||
#include "RakAlloca.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
// ****************************************************** PUBLIC METHODS ******************************************************
|
||||
|
||||
CCRakNetSlidingWindow::CCRakNetSlidingWindow()
|
||||
{
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
CCRakNetSlidingWindow::~CCRakNetSlidingWindow()
|
||||
{
|
||||
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::Init(CCTimeType curTime, uint32_t maxDatagramPayload)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
RTT=UNSET_TIME_US;
|
||||
MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload;
|
||||
cwnd=maxDatagramPayload;
|
||||
ssThresh=0.0;
|
||||
oldestUnsentAck=0;
|
||||
nextDatagramSequenceNumber=0;
|
||||
nextCongestionControlBlock=0;
|
||||
backoffThisBlock=speedUpThisBlock=false;
|
||||
expectedNextSequenceNumber=0;
|
||||
_isContinuousSend=false;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::Update(CCTimeType curTime, bool hasDataToSendOrResend)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) hasDataToSendOrResend;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
int CCRakNetSlidingWindow::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) isContinuousSend;
|
||||
(void) timeSinceLastTick;
|
||||
|
||||
return unacknowledgedBytes;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
int CCRakNetSlidingWindow::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) timeSinceLastTick;
|
||||
|
||||
_isContinuousSend=isContinuousSend;
|
||||
|
||||
if (unacknowledgedBytes<=cwnd)
|
||||
return (int) (cwnd-unacknowledgedBytes);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetSlidingWindow::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick)
|
||||
{
|
||||
CCTimeType rto = GetSenderRTOForACK();
|
||||
(void) estimatedTimeToNextTick;
|
||||
|
||||
// iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0
|
||||
if (rto==static_cast<double>(UNSET_TIME_US))
|
||||
{
|
||||
// Unknown how long until the remote system will retransmit, so better send right away
|
||||
return true;
|
||||
}
|
||||
|
||||
return curTime >= oldestUnsentAck + SYN;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
DatagramSequenceNumberType CCRakNetSlidingWindow::GetNextDatagramSequenceNumber(void)
|
||||
{
|
||||
return nextDatagramSequenceNumber;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
DatagramSequenceNumberType CCRakNetSlidingWindow::GetAndIncrementNextDatagramSequenceNumber(void)
|
||||
{
|
||||
DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber;
|
||||
nextDatagramSequenceNumber++;
|
||||
return dsnt;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnSendBytes(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) numBytes;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) sizeInBytes;
|
||||
(void) datagramSequenceNumber;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetSlidingWindow::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) sizeInBytes;
|
||||
(void) isContinuousSend;
|
||||
|
||||
if (oldestUnsentAck==0)
|
||||
oldestUnsentAck=curTime;
|
||||
|
||||
if (datagramSequenceNumber==expectedNextSequenceNumber)
|
||||
{
|
||||
*skippedMessageCount=0;
|
||||
expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1;
|
||||
}
|
||||
else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber))
|
||||
{
|
||||
*skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber;
|
||||
// Sanity check, just use timeout resend if this was really valid
|
||||
if (*skippedMessageCount>1000)
|
||||
{
|
||||
// During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice
|
||||
if (*skippedMessageCount>(uint32_t)50000)
|
||||
return false;
|
||||
*skippedMessageCount=1000;
|
||||
}
|
||||
expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*skippedMessageCount=0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnResend(CCTimeType curTime)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
if (_isContinuousSend && backoffThisBlock==false && cwnd>MAXIMUM_MTU_INCLUDING_UDP_HEADER*2)
|
||||
{
|
||||
ssThresh=cwnd/2;
|
||||
if (ssThresh<MAXIMUM_MTU_INCLUDING_UDP_HEADER)
|
||||
ssThresh=MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
cwnd=MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
|
||||
// Only backoff once per period
|
||||
backoffThisBlock=true;
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber)
|
||||
{
|
||||
(void) nakSequenceNumber;
|
||||
|
||||
OnResend(curTime);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond B, BytesPerMicrosecond AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber )
|
||||
{
|
||||
(void) B;
|
||||
(void) totalUserDataBytesAcked;
|
||||
(void) AS;
|
||||
(void) hasBAndAS;
|
||||
(void) curTime;
|
||||
(void) rtt;
|
||||
|
||||
RTT=(double) rtt;
|
||||
|
||||
_isContinuousSend=isContinuousSend;
|
||||
|
||||
if (isContinuousSend==false)
|
||||
return;
|
||||
|
||||
bool isNewCongestionControlPeriod;
|
||||
isNewCongestionControlPeriod = GreaterThan(sequenceNumber, nextCongestionControlBlock);
|
||||
|
||||
if (isNewCongestionControlPeriod)
|
||||
{
|
||||
nextCongestionControlBlock=nextDatagramSequenceNumber;
|
||||
backoffThisBlock=false;
|
||||
speedUpThisBlock=false;
|
||||
}
|
||||
|
||||
if (IsInSlowStart())
|
||||
{
|
||||
// if (isNewCongestionControlPeriod)
|
||||
{
|
||||
// Keep the number in range to avoid overflow
|
||||
if (cwnd<10000000)
|
||||
{
|
||||
cwnd*=2;
|
||||
if (cwnd>ssThresh && ssThresh!=0)
|
||||
{
|
||||
cwnd=ssThresh;
|
||||
cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isNewCongestionControlPeriod)
|
||||
cwnd+=MAXIMUM_MTU_INCLUDING_UDP_HEADER*MAXIMUM_MTU_INCLUDING_UDP_HEADER/cwnd;
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber )
|
||||
{
|
||||
(void) sequenceNumber;
|
||||
|
||||
OnResend(curTime);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *B, BytesPerMicrosecond *AS)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) B;
|
||||
(void) AS;
|
||||
|
||||
*hasBAndAS=false;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnSendAck(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) numBytes;
|
||||
|
||||
oldestUnsentAck=0;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::OnSendNACK(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) numBytes;
|
||||
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
CCTimeType CCRakNetSlidingWindow::GetRTOForRetransmission(void) const
|
||||
{
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
const CCTimeType maxThreshold=2000;
|
||||
const CCTimeType minThreshold=100;
|
||||
#else
|
||||
const CCTimeType maxThreshold=2000000;
|
||||
const CCTimeType minThreshold=100000;
|
||||
#endif
|
||||
|
||||
if (RTT==UNSET_TIME_US)
|
||||
{
|
||||
return maxThreshold;
|
||||
}
|
||||
|
||||
if (RTT * 3 > maxThreshold)
|
||||
return maxThreshold;
|
||||
if (RTT * 3 < minThreshold)
|
||||
return minThreshold;
|
||||
return (CCTimeType) RTT * 3;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetSlidingWindow::SetMTU(uint32_t bytes)
|
||||
{
|
||||
MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t CCRakNetSlidingWindow::GetMTU(void) const
|
||||
{
|
||||
return MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
BytesPerMicrosecond CCRakNetSlidingWindow::GetLocalReceiveRate(CCTimeType currentTime) const
|
||||
{
|
||||
(void) currentTime;
|
||||
|
||||
return 0; // TODO
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
double CCRakNetSlidingWindow::GetRTT(void) const
|
||||
{
|
||||
if (RTT==UNSET_TIME_US)
|
||||
return 0.0;
|
||||
return RTT;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetSlidingWindow::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b)
|
||||
{
|
||||
// a > b?
|
||||
const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2);
|
||||
return b!=a && b-a>halfSpan;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetSlidingWindow::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b)
|
||||
{
|
||||
// a < b?
|
||||
const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2;
|
||||
return b!=a && b-a<halfSpan;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
uint64_t CCRakNetSlidingWindow::GetBytesPerSecondLimitByCongestionControl(void) const
|
||||
{
|
||||
return 0; // TODO
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
CCTimeType CCRakNetSlidingWindow::GetSenderRTOForACK(void) const
|
||||
{
|
||||
if (RTT==UNSET_TIME_US)
|
||||
return (CCTimeType) UNSET_TIME_US;
|
||||
return (CCTimeType)(RTT + SYN);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetSlidingWindow::IsInSlowStart(void) const
|
||||
{
|
||||
return cwnd <= ssThresh || ssThresh==0;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
#endif
|
||||
209
third-party/RakNet/src/RakNet/CCRakNetSlidingWindow.hpp
vendored
Normal file
209
third-party/RakNet/src/RakNet/CCRakNetSlidingWindow.hpp
vendored
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
http://www.ssfnet.org/Exchange/tcp/tcpTutorialNotes.html
|
||||
|
||||
cwnd=max bytes allowed on wire at once
|
||||
|
||||
Start:
|
||||
cwnd=mtu
|
||||
ssthresh=unlimited
|
||||
|
||||
Slow start:
|
||||
On ack cwnd*=2
|
||||
|
||||
congestion avoidance:
|
||||
On ack during new period
|
||||
cwnd+=mtu*mtu/cwnd
|
||||
|
||||
on loss or duplicate ack during period:
|
||||
sshtresh=cwnd/2
|
||||
cwnd=MTU
|
||||
This reenters slow start
|
||||
|
||||
If cwnd < ssthresh, then use slow start
|
||||
else use congestion avoidance
|
||||
|
||||
|
||||
*/
|
||||
|
||||
#include "RakNetDefines.hpp"
|
||||
|
||||
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL==1
|
||||
|
||||
#ifndef __CONGESTION_CONTROL_SLIDING_WINDOW_H
|
||||
#define __CONGESTION_CONTROL_SLIDING_WINDOW_H
|
||||
|
||||
#include "NativeTypes.hpp"
|
||||
#include "RakNetTime.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
|
||||
/// Sizeof an UDP header in byte
|
||||
#define UDP_HEADER_SIZE 28
|
||||
|
||||
#define CC_DEBUG_PRINTF_1(x)
|
||||
#define CC_DEBUG_PRINTF_2(x,y)
|
||||
#define CC_DEBUG_PRINTF_3(x,y,z)
|
||||
#define CC_DEBUG_PRINTF_4(x,y,z,a)
|
||||
#define CC_DEBUG_PRINTF_5(x,y,z,a,b)
|
||||
//#define CC_DEBUG_PRINTF_1(x) printf(x)
|
||||
//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y)
|
||||
//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z)
|
||||
//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a)
|
||||
//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b)
|
||||
|
||||
/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0
|
||||
#define CC_TIME_TYPE_BYTES 8
|
||||
|
||||
|
||||
typedef RakNet::TimeUS CCTimeType;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef RakNet::uint24_t DatagramSequenceNumberType;
|
||||
typedef double BytesPerMicrosecond;
|
||||
typedef double BytesPerSecond;
|
||||
typedef double MicrosecondsPerByte;
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
class CCRakNetSlidingWindow
|
||||
{
|
||||
public:
|
||||
|
||||
CCRakNetSlidingWindow();
|
||||
~CCRakNetSlidingWindow();
|
||||
|
||||
/// Reset all variables to their initial states, for a new connection
|
||||
void Init(CCTimeType curTime, uint32_t maxDatagramPayload);
|
||||
|
||||
/// Update over time
|
||||
void Update(CCTimeType curTime, bool hasDataToSendOrResend);
|
||||
|
||||
int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
|
||||
int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
|
||||
|
||||
/// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time
|
||||
/// This reduces overall bandwidth usage
|
||||
/// How long they can be buffered depends on the retransmit time of the sender
|
||||
/// Should call once per update tick, and send if needed
|
||||
bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick);
|
||||
|
||||
/// Every data packet sent must contain a sequence number
|
||||
/// Call this function to get it. The sequence number is passed into OnGotPacketPair()
|
||||
DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void);
|
||||
DatagramSequenceNumberType GetNextDatagramSequenceNumber(void);
|
||||
|
||||
/// Call this when you send packets
|
||||
/// Every 15th and 16th packets should be sent as a packet pair if possible
|
||||
/// When packets marked as a packet pair arrive, pass to OnGotPacketPair()
|
||||
/// When any packets arrive, (additionally) pass to OnGotPacket
|
||||
/// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck()
|
||||
void OnSendBytes(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Call this when you get a packet pair
|
||||
void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime);
|
||||
|
||||
/// Call this when you get a packet (including packet pairs)
|
||||
/// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero
|
||||
/// In that case, send a NAK for every sequence number up to that count
|
||||
bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount);
|
||||
|
||||
/// Call when you get a NAK, with the sequence number of the lost message
|
||||
/// Affects the congestion control
|
||||
void OnResend(CCTimeType curTime);
|
||||
void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber);
|
||||
|
||||
/// Call this when an ACK arrives.
|
||||
/// hasBAndAS are possibly written with the ack, see OnSendAck()
|
||||
/// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn
|
||||
/// B and AS are updated at most once per SYN
|
||||
void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond B, BytesPerMicrosecond AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber );
|
||||
void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber );
|
||||
|
||||
/// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted
|
||||
/// Call before calling OnSendAck()
|
||||
void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *B, BytesPerMicrosecond *AS);
|
||||
|
||||
/// Call when we send an ack, to write B and AS if needed
|
||||
/// B and AS are only written once per SYN, to prevent slow calculations
|
||||
/// Also updates SND, the period between sends, since data is written out
|
||||
/// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes
|
||||
void OnSendAck(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Call when we send a NACK
|
||||
/// Also updates SND, the period between sends, since data is written out
|
||||
void OnSendNACK(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Retransmission time out for the sender
|
||||
/// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control
|
||||
/// RTO = (RTT + 4 * RTTVar) + SYN
|
||||
/// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2;
|
||||
/// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED
|
||||
/// Minimum value is 100 milliseconds
|
||||
CCTimeType GetRTOForRetransmission(void) const;
|
||||
|
||||
/// Set the maximum amount of data that can be sent in one datagram
|
||||
/// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE
|
||||
void SetMTU(uint32_t bytes);
|
||||
|
||||
/// Return what was set by SetMTU()
|
||||
uint32_t GetMTU(void) const;
|
||||
|
||||
/// Query for statistics
|
||||
BytesPerMicrosecond GetLocalSendRate(void) const {return 0;}
|
||||
BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const;
|
||||
BytesPerMicrosecond GetRemoveReceiveRate(void) const {return 0;}
|
||||
//BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;}
|
||||
BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;}
|
||||
double GetLinkCapacityBytesPerSecond(void) const {return 0;}
|
||||
|
||||
/// Query for statistics
|
||||
double GetRTT(void) const;
|
||||
|
||||
bool GetIsInSlowStart(void) const {return IsInSlowStart();}
|
||||
uint32_t GetCWNDLimit(void) const {return (uint32_t) 0;}
|
||||
|
||||
|
||||
/// Is a > b, accounting for variable overflow?
|
||||
static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
|
||||
/// Is a < b, accounting for variable overflow?
|
||||
static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
|
||||
// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond);
|
||||
uint64_t GetBytesPerSecondLimitByCongestionControl(void) const;
|
||||
|
||||
protected:
|
||||
|
||||
// Maximum amount of bytes that the user can send, e.g. the size of one full datagram
|
||||
uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
|
||||
double RTT;
|
||||
|
||||
double cwnd; // max bytes on wire
|
||||
double ssThresh; // Threshhold between slow start and congestion avoidance
|
||||
|
||||
/// When we get an ack, if oldestUnsentAck==0, set it to the current time
|
||||
/// When we send out acks, set oldestUnsentAck to 0
|
||||
CCTimeType oldestUnsentAck;
|
||||
|
||||
CCTimeType GetSenderRTOForACK(void) const;
|
||||
|
||||
/// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment
|
||||
DatagramSequenceNumberType nextDatagramSequenceNumber;
|
||||
DatagramSequenceNumberType nextCongestionControlBlock;
|
||||
bool backoffThisBlock, speedUpThisBlock;
|
||||
/// Track which datagram sequence numbers have arrived.
|
||||
/// If a sequence number is skipped, send a NAK for all skipped messages
|
||||
DatagramSequenceNumberType expectedNextSequenceNumber;
|
||||
|
||||
bool _isContinuousSend;
|
||||
|
||||
bool IsInSlowStart(void) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
798
third-party/RakNet/src/RakNet/CCRakNetUDT.cpp
vendored
Normal file
798
third-party/RakNet/src/RakNet/CCRakNetUDT.cpp
vendored
Normal file
@@ -0,0 +1,798 @@
|
||||
#include "CCRakNetUDT.hpp"
|
||||
|
||||
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1
|
||||
|
||||
#include "Rand.hpp"
|
||||
#include "MTUSize.hpp"
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
//#include <memory.h>
|
||||
#include "RakAssert.hpp"
|
||||
#include "RakAlloca.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
static const double UNSET_TIME_US=-1;
|
||||
static const double CWND_MIN_THRESHOLD=2.0;
|
||||
static const double UNDEFINED_TRANSFER_RATE=0.0;
|
||||
/// Interval at which to update aspects of the system
|
||||
/// 1. send acks
|
||||
/// 2. update time interval between outgoing packets
|
||||
/// 3, Yodate retransmit timeout
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
static const CCTimeType SYN=10;
|
||||
#else
|
||||
static const CCTimeType SYN=10000;
|
||||
#endif
|
||||
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
#define MAX_RTT 1000
|
||||
#define RTT_TOLERANCE 30
|
||||
#else
|
||||
#define MAX_RTT 1000000
|
||||
#define RTT_TOLERANCE 30000
|
||||
#endif
|
||||
|
||||
|
||||
double RTTVarMultiple=4.0;
|
||||
|
||||
|
||||
// ****************************************************** PUBLIC METHODS ******************************************************
|
||||
|
||||
CCRakNetUDT::CCRakNetUDT()
|
||||
{
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
CCRakNetUDT::~CCRakNetUDT()
|
||||
{
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::Init(CCTimeType curTime, uint32_t maxDatagramPayload)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
nextSYNUpdate=0;
|
||||
packetPairRecieptHistoryWriteIndex=0;
|
||||
packetArrivalHistoryWriteIndex=0;
|
||||
packetArrivalHistoryWriteCount=0;
|
||||
RTT=UNSET_TIME_US;
|
||||
// RTTVar=UNSET_TIME_US;
|
||||
isInSlowStart=true;
|
||||
NAKCount=1000;
|
||||
AvgNAKNum=1;
|
||||
DecInterval=1;
|
||||
DecCount=0;
|
||||
nextDatagramSequenceNumber=0;
|
||||
lastPacketPairPacketArrivalTime=0;
|
||||
lastPacketPairSequenceNumber=(DatagramSequenceNumberType)(const uint32_t)-1;
|
||||
lastPacketArrivalTime=0;
|
||||
CWND=CWND_MIN_THRESHOLD;
|
||||
lastUpdateWindowSizeAndAck=0;
|
||||
lastTransmitOfBAndAS=0;
|
||||
ExpCount=1.0;
|
||||
totalUserDataBytesSent=0;
|
||||
oldestUnsentAck=0;
|
||||
MAXIMUM_MTU_INCLUDING_UDP_HEADER=maxDatagramPayload;
|
||||
CWND_MAX_THRESHOLD=RESEND_BUFFER_ARRAY_LENGTH;
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) 3.6;
|
||||
#else
|
||||
const BytesPerMicrosecond DEFAULT_TRANSFER_RATE=(BytesPerMicrosecond) .0036;
|
||||
#endif
|
||||
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
lastRttOnIncreaseSendRate=1000;
|
||||
#else
|
||||
lastRttOnIncreaseSendRate=1000000;
|
||||
#endif
|
||||
nextCongestionControlBlock=0;
|
||||
lastRtt=0;
|
||||
|
||||
// B=DEFAULT_TRANSFER_RATE;
|
||||
AS=UNDEFINED_TRANSFER_RATE;
|
||||
const MicrosecondsPerByte DEFAULT_BYTE_INTERVAL=(MicrosecondsPerByte) (1.0/DEFAULT_TRANSFER_RATE);
|
||||
SND=DEFAULT_BYTE_INTERVAL;
|
||||
expectedNextSequenceNumber=0;
|
||||
sendBAndASCount=0;
|
||||
packetArrivalHistoryContinuousGapsIndex=0;
|
||||
//packetPairRecipetHistoryGapsIndex=0;
|
||||
hasWrittenToPacketPairReceiptHistory=false;
|
||||
InitPacketArrivalHistory();
|
||||
|
||||
estimatedLinkCapacityBytesPerSecond=0;
|
||||
bytesCanSendThisTick=0;
|
||||
hadPacketlossThisBlock=false;
|
||||
pingsLastInterval.Clear(__FILE__,__LINE__);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::SetMTU(uint32_t bytes)
|
||||
{
|
||||
MAXIMUM_MTU_INCLUDING_UDP_HEADER=bytes;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
uint32_t CCRakNetUDT::GetMTU(void) const
|
||||
{
|
||||
return MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::Update(CCTimeType curTime, bool hasDataToSendOrResend)
|
||||
{
|
||||
(void) hasDataToSendOrResend;
|
||||
(void) curTime;
|
||||
|
||||
return;
|
||||
|
||||
// I suspect this is causing major lag
|
||||
|
||||
/*
|
||||
if (hasDataToSendOrResend==false)
|
||||
halveSNDOnNoDataTime=0;
|
||||
else if (halveSNDOnNoDataTime==0)
|
||||
{
|
||||
UpdateHalveSNDOnNoDataTime(curTime);
|
||||
ExpCount=1.0;
|
||||
}
|
||||
|
||||
// If you send, and get no data at all from that time to RTO, then halve send rate7
|
||||
if (HasHalveSNDOnNoDataTimeElapsed(curTime))
|
||||
{
|
||||
/// 2000 bytes per second
|
||||
/// 0.0005 seconds per byte
|
||||
/// 0.5 milliseconds per byte
|
||||
/// 500 microseconds per byte
|
||||
// printf("No incoming data, halving send rate\n");
|
||||
SND*=2.0;
|
||||
CapMinSnd(_FILE_AND_LINE_);
|
||||
ExpCount+=1.0;
|
||||
if (ExpCount>8.0)
|
||||
ExpCount=8.0;
|
||||
|
||||
UpdateHalveSNDOnNoDataTime(curTime);
|
||||
}
|
||||
*/
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
int CCRakNetUDT::GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
if (isInSlowStart)
|
||||
{
|
||||
uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);
|
||||
return CWNDLimit;
|
||||
}
|
||||
return GetTransmissionBandwidth(curTime,timeSinceLastTick,unacknowledgedBytes,isContinuousSend);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
int CCRakNetUDT::GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
if (isInSlowStart)
|
||||
{
|
||||
uint32_t CWNDLimit = (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER-unacknowledgedBytes);
|
||||
return CWNDLimit;
|
||||
}
|
||||
if (bytesCanSendThisTick>0)
|
||||
bytesCanSendThisTick=0;
|
||||
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
if (isContinuousSend==false && timeSinceLastTick>100)
|
||||
timeSinceLastTick=100;
|
||||
#else
|
||||
if (isContinuousSend==false && timeSinceLastTick>100000)
|
||||
timeSinceLastTick=100000;
|
||||
#endif
|
||||
|
||||
bytesCanSendThisTick=(int)((double)timeSinceLastTick*((double)1.0/SND)+(double)bytesCanSendThisTick);
|
||||
if (bytesCanSendThisTick>0)
|
||||
return bytesCanSendThisTick;
|
||||
return 0;
|
||||
}
|
||||
uint64_t CCRakNetUDT::GetBytesPerSecondLimitByCongestionControl(void) const
|
||||
{
|
||||
if (isInSlowStart)
|
||||
return 0;
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
return (uint64_t) ((double)1.0/(SND*1000.0));
|
||||
#else
|
||||
return (uint64_t) ((double)1.0/(SND*1000000.0));
|
||||
#endif
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetUDT::ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick)
|
||||
{
|
||||
CCTimeType rto = GetSenderRTOForACK();
|
||||
|
||||
// iphone crashes on comparison between double and int64 http://www.jenkinssoftware.com/forum/index.php?topic=2717.0
|
||||
if (rto==static_cast<double>(UNSET_TIME_US))
|
||||
{
|
||||
// Unknown how long until the remote system will retransmit, so better send right away
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// CCTimeType remoteRetransmitTime=oldestUnsentAck+rto-RTT*.5;
|
||||
// CCTimeType ackArrivalTimeIfWeDelay=RTT*.5+estimatedTimeToNextTick+curTime;
|
||||
// return ackArrivalTimeIfWeDelay<remoteRetransmitTime;
|
||||
|
||||
// Simplified equation
|
||||
// GU: At least one ACK should be sent per SYN, otherwise your protocol will increase slower.
|
||||
return curTime >= oldestUnsentAck + SYN ||
|
||||
estimatedTimeToNextTick+curTime < oldestUnsentAck+rto-RTT;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
DatagramSequenceNumberType CCRakNetUDT::GetNextDatagramSequenceNumber(void)
|
||||
{
|
||||
return nextDatagramSequenceNumber;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
DatagramSequenceNumberType CCRakNetUDT::GetAndIncrementNextDatagramSequenceNumber(void)
|
||||
{
|
||||
DatagramSequenceNumberType dsnt=nextDatagramSequenceNumber;
|
||||
nextDatagramSequenceNumber++;
|
||||
return dsnt;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnSendBytes(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
totalUserDataBytesSent+=numBytes;
|
||||
if (isInSlowStart==false)
|
||||
bytesCanSendThisTick-=numBytes;
|
||||
}
|
||||
|
||||
// ****************************************************** PROTECTED METHODS ******************************************************
|
||||
|
||||
void CCRakNetUDT::SetNextSYNUpdate(CCTimeType currentTime)
|
||||
{
|
||||
nextSYNUpdate+=SYN;
|
||||
if (nextSYNUpdate < currentTime)
|
||||
nextSYNUpdate=currentTime+SYN;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRate(CCTimeType curTime) const
|
||||
{
|
||||
(void) curTime;
|
||||
// Not an instantaneous measurement
|
||||
/*
|
||||
if (continuousBytesReceivedStartTime!=0 && curTime>continuousBytesReceivedStartTime)
|
||||
{
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
const CCTimeType threshold=100;
|
||||
#else
|
||||
const CCTimeType threshold=100000;
|
||||
#endif
|
||||
if (curTime-continuousBytesReceivedStartTime>threshold)
|
||||
return (BytesPerMicrosecond) continuousBytesReceived/(BytesPerMicrosecond) (curTime-continuousBytesReceivedStartTime);
|
||||
}
|
||||
|
||||
return UNDEFINED_TRANSFER_RATE;
|
||||
*/
|
||||
|
||||
|
||||
if (packetArrivalHistoryWriteCount<CC_RAKNET_UDT_PACKET_HISTORY_LENGTH)
|
||||
return UNDEFINED_TRANSFER_RATE;
|
||||
|
||||
BytesPerMicrosecond median = ReceiverCalculateDataArrivalRateMedian();
|
||||
int i;
|
||||
const BytesPerMicrosecond oneEighthMedian=median*(1.0/8.0);
|
||||
const BytesPerMicrosecond eightTimesMedian=median*8.0f;
|
||||
BytesPerMicrosecond medianListLength=0.0;
|
||||
BytesPerMicrosecond sum=0.0;
|
||||
// Find average of acceptedMedianValues
|
||||
for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++)
|
||||
{
|
||||
if (packetArrivalHistory[i]>=oneEighthMedian &&
|
||||
packetArrivalHistory[i]<eightTimesMedian)
|
||||
{
|
||||
medianListLength=medianListLength+1.0;
|
||||
sum+=packetArrivalHistory[i];
|
||||
}
|
||||
}
|
||||
if (medianListLength==0.0)
|
||||
return UNDEFINED_TRANSFER_RATE;
|
||||
return sum/medianListLength;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
BytesPerMicrosecond CCRakNetUDT::ReceiverCalculateDataArrivalRateMedian(void) const
|
||||
{
|
||||
return CalculateListMedianRecursive(packetArrivalHistory, CC_RAKNET_UDT_PACKET_HISTORY_LENGTH, 0, 0);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
BytesPerMicrosecond CCRakNetUDT::CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum)
|
||||
{
|
||||
BytesPerMicrosecond lessThanMedian[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], greaterThanMedian[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH];
|
||||
int lessThanMedianListLength=0, greaterThanMedianListLength=0;
|
||||
BytesPerMicrosecond median=inputList[0];
|
||||
int i;
|
||||
for (i=1; i < inputListLength; i++)
|
||||
{
|
||||
// If same value, spread among lists evenly
|
||||
if (inputList[i]<median || ((i&1)==0 && inputList[i]==median))
|
||||
lessThanMedian[lessThanMedianListLength++]=inputList[i];
|
||||
else
|
||||
greaterThanMedian[greaterThanMedianListLength++]=inputList[i];
|
||||
}
|
||||
RakAssert(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH%2==0);
|
||||
if (lessThanMedianListLength+lessThanSum==greaterThanMedianListLength+greaterThanSum+1 ||
|
||||
lessThanMedianListLength+lessThanSum==greaterThanMedianListLength+greaterThanSum-1)
|
||||
return median;
|
||||
|
||||
if (lessThanMedianListLength+lessThanSum < greaterThanMedianListLength+greaterThanSum)
|
||||
{
|
||||
lessThanMedian[lessThanMedianListLength++]=median;
|
||||
return CalculateListMedianRecursive(greaterThanMedian, greaterThanMedianListLength, lessThanMedianListLength+lessThanSum, greaterThanSum);
|
||||
}
|
||||
else
|
||||
{
|
||||
greaterThanMedian[greaterThanMedianListLength++]=median;
|
||||
return CalculateListMedianRecursive(lessThanMedian, lessThanMedianListLength, lessThanSum, greaterThanMedianListLength+greaterThanSum);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetUDT::GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b)
|
||||
{
|
||||
// a > b?
|
||||
const DatagramSequenceNumberType halfSpan =(DatagramSequenceNumberType) (((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2);
|
||||
return b!=a && b-a>halfSpan;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetUDT::LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b)
|
||||
{
|
||||
// a < b?
|
||||
const DatagramSequenceNumberType halfSpan = ((DatagramSequenceNumberType)(const uint32_t)-1)/(DatagramSequenceNumberType)2;
|
||||
return b!=a && b-a<halfSpan;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
CCTimeType CCRakNetUDT::GetSenderRTOForACK(void) const
|
||||
{
|
||||
if (RTT==UNSET_TIME_US)
|
||||
return (CCTimeType) UNSET_TIME_US;
|
||||
double RTTVar = maxRTT-minRTT;
|
||||
return (CCTimeType)(RTT + RTTVarMultiple * RTTVar + SYN);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
CCTimeType CCRakNetUDT::GetRTOForRetransmission(void) const
|
||||
{
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
const CCTimeType maxThreshold=10000;
|
||||
const CCTimeType minThreshold=100;
|
||||
#else
|
||||
const CCTimeType maxThreshold=1000000;
|
||||
const CCTimeType minThreshold=100000;
|
||||
#endif
|
||||
|
||||
if (RTT==UNSET_TIME_US)
|
||||
{
|
||||
return (CCTimeType) maxThreshold;
|
||||
}
|
||||
|
||||
CCTimeType ret = lastRttOnIncreaseSendRate*2;
|
||||
|
||||
if (ret<minThreshold)
|
||||
return minThreshold;
|
||||
if (ret>maxThreshold)
|
||||
return maxThreshold;
|
||||
return ret;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnResend(CCTimeType curTime)
|
||||
{
|
||||
(void) curTime;
|
||||
|
||||
if (isInSlowStart)
|
||||
{
|
||||
if (AS!=UNDEFINED_TRANSFER_RATE)
|
||||
EndSlowStart();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hadPacketlossThisBlock==false)
|
||||
{
|
||||
// Logging
|
||||
// printf("Sending SLOWER due to Resend, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt );
|
||||
|
||||
IncreaseTimeBetweenSends();
|
||||
hadPacketlossThisBlock=true;
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber)
|
||||
{
|
||||
(void) nakSequenceNumber;
|
||||
(void) curTime;
|
||||
|
||||
if (isInSlowStart)
|
||||
{
|
||||
if (AS!=UNDEFINED_TRANSFER_RATE)
|
||||
EndSlowStart();
|
||||
return;
|
||||
}
|
||||
|
||||
if (hadPacketlossThisBlock==false)
|
||||
{
|
||||
// Logging
|
||||
//printf("Sending SLOWER due to NAK, Rate=%f MBPS. Rtt=%i\n", GetLocalSendRate(), lastRtt );
|
||||
if (pingsLastInterval.Size()>10)
|
||||
{
|
||||
for (int i=0; i < 10; i++)
|
||||
printf("%i, ", pingsLastInterval[pingsLastInterval.Size()-1-i]/1000);
|
||||
}
|
||||
printf("\n");
|
||||
IncreaseTimeBetweenSends();
|
||||
|
||||
hadPacketlossThisBlock=true;
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::EndSlowStart(void)
|
||||
{
|
||||
RakAssert(isInSlowStart==true);
|
||||
RakAssert(AS!=UNDEFINED_TRANSFER_RATE);
|
||||
|
||||
// This overestimates
|
||||
estimatedLinkCapacityBytesPerSecond=AS * 1000000.0;
|
||||
|
||||
isInSlowStart=false;
|
||||
SND=1.0/AS;
|
||||
CapMinSnd(_FILE_AND_LINE_);
|
||||
|
||||
// printf("ENDING SLOW START\n");
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
// printf("Initial SND=%f Kilobytes per second\n", 1.0/SND);
|
||||
#else
|
||||
// printf("Initial SND=%f Megabytes per second\n", 1.0/SND);
|
||||
#endif
|
||||
if (SND > .1)
|
||||
PrintLowBandwidthWarning();
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime)
|
||||
{
|
||||
(void) datagramSequenceNumber;
|
||||
(void) sizeInBytes;
|
||||
(void) curTime;
|
||||
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
bool CCRakNetUDT::OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount)
|
||||
{
|
||||
CC_DEBUG_PRINTF_2("R%i ",datagramSequenceNumber.val);
|
||||
|
||||
if (datagramSequenceNumber==expectedNextSequenceNumber)
|
||||
{
|
||||
*skippedMessageCount=0;
|
||||
expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1;
|
||||
}
|
||||
else if (GreaterThan(datagramSequenceNumber, expectedNextSequenceNumber))
|
||||
{
|
||||
*skippedMessageCount=datagramSequenceNumber-expectedNextSequenceNumber;
|
||||
// Sanity check, just use timeout resend if this was really valid
|
||||
if (*skippedMessageCount>1000)
|
||||
{
|
||||
// During testing, the nat punchthrough server got 51200 on the first packet. I have no idea where this comes from, but has happened twice
|
||||
if (*skippedMessageCount>(uint32_t)50000)
|
||||
return false;
|
||||
*skippedMessageCount=1000;
|
||||
}
|
||||
expectedNextSequenceNumber=datagramSequenceNumber+(DatagramSequenceNumberType)1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*skippedMessageCount=0;
|
||||
}
|
||||
|
||||
if (curTime>lastPacketArrivalTime)
|
||||
{
|
||||
CCTimeType interval = curTime-lastPacketArrivalTime;
|
||||
|
||||
// printf("Packet arrival gap is %I64u\n", (interval));
|
||||
|
||||
if (isContinuousSend)
|
||||
{
|
||||
continuousBytesReceived+=sizeInBytes;
|
||||
if (continuousBytesReceivedStartTime==0)
|
||||
continuousBytesReceivedStartTime=lastPacketArrivalTime;
|
||||
|
||||
|
||||
mostRecentPacketArrivalHistory=(BytesPerMicrosecond)sizeInBytes/(BytesPerMicrosecond)interval;
|
||||
|
||||
// if (mostRecentPacketArrivalHistory < (BytesPerMicrosecond)0.0035)
|
||||
// {
|
||||
// printf("%s:%i LIKELY BUG: Calculated packetArrivalHistory is below 28.8 Kbps modem\nReport to rakkar@jenkinssoftware.com with file and line number\n", _FILE_AND_LINE_);
|
||||
// }
|
||||
|
||||
packetArrivalHistoryContinuousGaps[packetArrivalHistoryContinuousGapsIndex++]=(int) interval;
|
||||
packetArrivalHistoryContinuousGapsIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1);
|
||||
|
||||
packetArrivalHistoryWriteCount++;
|
||||
packetArrivalHistory[packetArrivalHistoryWriteIndex++]=mostRecentPacketArrivalHistory;
|
||||
// Wrap to 0 at the end of the range
|
||||
// Assumes power of 2 for CC_RAKNET_UDT_PACKET_HISTORY_LENGTH
|
||||
packetArrivalHistoryWriteIndex&=(CC_RAKNET_UDT_PACKET_HISTORY_LENGTH-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
continuousBytesReceivedStartTime=0;
|
||||
continuousBytesReceived=0;
|
||||
}
|
||||
|
||||
lastPacketArrivalTime=curTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber )
|
||||
{
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
RakAssert(rtt < 10000);
|
||||
#else
|
||||
RakAssert(rtt < 10000000);
|
||||
#endif
|
||||
(void) _B;
|
||||
|
||||
if (hasBAndAS)
|
||||
{
|
||||
/// RakAssert(_B!=UNDEFINED_TRANSFER_RATE && _AS!=UNDEFINED_TRANSFER_RATE);
|
||||
// B=B * .875 + _B * .125;
|
||||
// AS is packet arrival rate
|
||||
RakAssert(_AS!=UNDEFINED_TRANSFER_RATE);
|
||||
AS=_AS;
|
||||
CC_DEBUG_PRINTF_4("ArrivalRate=%f linkCap=%f incomingLinkCap=%f\n", _AS,B,_B);
|
||||
}
|
||||
|
||||
if (oldestUnsentAck==0)
|
||||
oldestUnsentAck=curTime;
|
||||
|
||||
if (isInSlowStart==true)
|
||||
{
|
||||
nextCongestionControlBlock=nextDatagramSequenceNumber;
|
||||
lastRttOnIncreaseSendRate=rtt;
|
||||
UpdateWindowSizeAndAckOnAckPreSlowStart(totalUserDataBytesAcked);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateWindowSizeAndAckOnAckPerSyn(curTime, rtt, isContinuousSend, sequenceNumber);
|
||||
}
|
||||
|
||||
lastUpdateWindowSizeAndAck=curTime;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS)
|
||||
{
|
||||
if (curTime>lastTransmitOfBAndAS+SYN)
|
||||
{
|
||||
*_B=0;
|
||||
*_AS=ReceiverCalculateDataArrivalRate(curTime);
|
||||
|
||||
if (*_AS==UNDEFINED_TRANSFER_RATE)
|
||||
{
|
||||
*hasBAndAS=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
*hasBAndAS=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*hasBAndAS=false;
|
||||
}
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnSendAck(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) numBytes;
|
||||
(void) curTime;
|
||||
|
||||
// This is not accounted for on the remote system, and thus causes bandwidth to be underutilized
|
||||
//UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE);
|
||||
|
||||
oldestUnsentAck=0;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::OnSendNACK(CCTimeType curTime, uint32_t numBytes)
|
||||
{
|
||||
(void) numBytes;
|
||||
(void) curTime;
|
||||
|
||||
// This is not accounted for on the remote system, and thus causes bandwidth to be underutilized
|
||||
// UpdateNextAllowedSend(curTime, numBytes+UDP_HEADER_SIZE);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked)
|
||||
{
|
||||
// During slow start, max window size is the number of full packets that have been sent out
|
||||
// CWND=(double) ((double)totalUserDataBytesSent/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER);
|
||||
CC_DEBUG_PRINTF_3("CWND increasing from %f to %f\n", CWND, (double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER));
|
||||
CWND=(double) ((double)totalUserDataBytesAcked/(double)MAXIMUM_MTU_INCLUDING_UDP_HEADER);
|
||||
if (CWND>=CWND_MAX_THRESHOLD)
|
||||
{
|
||||
CWND=CWND_MAX_THRESHOLD;
|
||||
|
||||
if (AS!=UNDEFINED_TRANSFER_RATE)
|
||||
EndSlowStart();
|
||||
}
|
||||
if (CWND<CWND_MIN_THRESHOLD)
|
||||
CWND=CWND_MIN_THRESHOLD;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber)
|
||||
{
|
||||
(void) curTime;
|
||||
(void) sequenceNumber;
|
||||
if (isContinuousSend==false)
|
||||
{
|
||||
nextCongestionControlBlock=nextDatagramSequenceNumber;
|
||||
pingsLastInterval.Clear(__FILE__,__LINE__);
|
||||
return;
|
||||
}
|
||||
|
||||
pingsLastInterval.Push(rtt,__FILE__,__LINE__);
|
||||
static const int intervalSize=33; // Should be odd
|
||||
if (pingsLastInterval.Size()>intervalSize)
|
||||
pingsLastInterval.Pop();
|
||||
if (GreaterThan(sequenceNumber, nextCongestionControlBlock) &&
|
||||
sequenceNumber-nextCongestionControlBlock>=intervalSize &&
|
||||
pingsLastInterval.Size()==intervalSize)
|
||||
{
|
||||
double slopeSum=0.0;
|
||||
double average=(double) pingsLastInterval[0];
|
||||
int sampleSize=pingsLastInterval.Size();
|
||||
for (int i=1; i < sampleSize; i++)
|
||||
{
|
||||
slopeSum+=(double)pingsLastInterval[i]-(double)pingsLastInterval[i-1];
|
||||
average+=pingsLastInterval[i];
|
||||
}
|
||||
average/=sampleSize;
|
||||
|
||||
if (hadPacketlossThisBlock==true)
|
||||
{
|
||||
}
|
||||
else if (slopeSum < -.10*average)
|
||||
{
|
||||
// Logging
|
||||
//printf("Ping dropping. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt );
|
||||
}
|
||||
else if (slopeSum > .10*average)
|
||||
{
|
||||
// Logging
|
||||
//printf("Ping rising. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt );
|
||||
IncreaseTimeBetweenSends();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Logging
|
||||
//printf("Ping stable. slope=%f%%. Rate=%f MBPS. Rtt=%i\n", 100.0*slopeSum/average, GetLocalSendRate(), rtt );
|
||||
|
||||
// No packetloss over time threshhold, and rtt decreased, so send faster
|
||||
lastRttOnIncreaseSendRate=rtt;
|
||||
DecreaseTimeBetweenSends();
|
||||
}
|
||||
|
||||
pingsLastInterval.Clear(__FILE__,__LINE__);
|
||||
hadPacketlossThisBlock=false;
|
||||
nextCongestionControlBlock=nextDatagramSequenceNumber;
|
||||
}
|
||||
|
||||
lastRtt=rtt;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
double CCRakNetUDT::BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in)
|
||||
{
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
const BytesPerMicrosecond factor = 1.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
#else
|
||||
const BytesPerMicrosecond factor = 1000.0 / (BytesPerMicrosecond) MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
#endif
|
||||
return in * factor;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::InitPacketArrivalHistory(void)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < CC_RAKNET_UDT_PACKET_HISTORY_LENGTH; i++)
|
||||
{
|
||||
packetArrivalHistory[i]=UNDEFINED_TRANSFER_RATE;
|
||||
packetArrivalHistoryContinuousGaps[i]=0;
|
||||
}
|
||||
|
||||
packetArrivalHistoryWriteCount=0;
|
||||
continuousBytesReceived=0;
|
||||
continuousBytesReceivedStartTime=0;
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------------------------------------------
|
||||
void CCRakNetUDT::PrintLowBandwidthWarning(void)
|
||||
{
|
||||
|
||||
/*
|
||||
printf("\n-------LOW BANDWIDTH -----\n");
|
||||
if (isInSlowStart==false)
|
||||
printf("SND=%f Megabytes per second\n", 1.0/SND);
|
||||
printf("Window size=%f\n", CWND);
|
||||
printf("Pipe from packet pair = %f megabytes per second\n", B);
|
||||
printf("RTT=%f milliseconds\n", RTT/1000.0);
|
||||
printf("RTT Variance=%f milliseconds\n", RTTVar/1000.0);
|
||||
printf("Retransmission=%i milliseconds\n", GetRTOForRetransmission()/1000);
|
||||
printf("Packet arrival rate on the remote system=%f megabytes per second\n", AS);
|
||||
printf("Packet arrival rate on our system=%f megabytes per second\n", ReceiverCalculateDataArrivalRate());
|
||||
printf("isInSlowStart=%i\n", isInSlowStart);
|
||||
printf("---------------\n");
|
||||
*/
|
||||
}
|
||||
BytesPerMicrosecond CCRakNetUDT::GetLocalReceiveRate(CCTimeType currentTime) const
|
||||
{
|
||||
return ReceiverCalculateDataArrivalRate(currentTime);
|
||||
}
|
||||
double CCRakNetUDT::GetRTT(void) const
|
||||
{
|
||||
if (RTT==UNSET_TIME_US)
|
||||
return 0.0;
|
||||
return RTT;
|
||||
}
|
||||
void CCRakNetUDT::CapMinSnd(const char *file, int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
|
||||
if (SND > 500)
|
||||
{
|
||||
SND=500;
|
||||
CC_DEBUG_PRINTF_3("%s:%i LIKELY BUG: SND has gotten above 500 microseconds between messages (28.8 modem)\nReport to rakkar@jenkinssoftware.com with file and line number\n", file, line);
|
||||
}
|
||||
}
|
||||
void CCRakNetUDT::IncreaseTimeBetweenSends(void)
|
||||
{
|
||||
// In order to converge, bigger numbers have to increase slower and decrease faster
|
||||
// SND==500 then increment is .02
|
||||
// SND==0 then increment is near 0
|
||||
// (SND+1.0) brings it to the range of 1 to 501
|
||||
// Square the number, which is the range of 1 to 251001
|
||||
// Divide by 251001, which is the range of 1/251001 to 1
|
||||
|
||||
double increment;
|
||||
increment = .02 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ;
|
||||
// SND=500 then increment=.02
|
||||
// SND=0 then increment=near 0
|
||||
SND*=(1.02 - increment);
|
||||
|
||||
// SND=0 then fast increase, slow decrease
|
||||
// SND=500 then slow increase, fast decrease
|
||||
CapMinSnd(__FILE__,__LINE__);
|
||||
}
|
||||
void CCRakNetUDT::DecreaseTimeBetweenSends(void)
|
||||
{
|
||||
double increment;
|
||||
increment = .01 * ((SND+1.0) * (SND+1.0)) / (501.0*501.0) ;
|
||||
// SND=500 then increment=.01
|
||||
// SND=0 then increment=near 0
|
||||
SND*=(.99 - increment);
|
||||
}
|
||||
/*
|
||||
void CCRakNetUDT::SetTimeBetweenSendsLimit(unsigned int bitsPerSecond)
|
||||
{
|
||||
// bitsPerSecond / 1000000 = bitsPerMicrosecond
|
||||
// bitsPerMicrosecond / 8 = BytesPerMicrosecond
|
||||
// 1 / BytesPerMicrosecond = MicrosecondsPerByte
|
||||
// 1 / ( (bitsPerSecond / 1000000) / 8 ) =
|
||||
// 1 / (bitsPerSecond / 8000000) =
|
||||
// 8000000 / bitsPerSecond
|
||||
|
||||
#if CC_TIME_TYPE_BYTES==4
|
||||
MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000 / (MicrosecondsPerByte)bitsPerSecond;
|
||||
#else
|
||||
MicrosecondsPerByte limit = (MicrosecondsPerByte) 8000000 / (MicrosecondsPerByte)bitsPerSecond;
|
||||
#endif
|
||||
if (limit > SND)
|
||||
SND=limit;
|
||||
}
|
||||
*/
|
||||
|
||||
#endif
|
||||
394
third-party/RakNet/src/RakNet/CCRakNetUDT.hpp
vendored
Normal file
394
third-party/RakNet/src/RakNet/CCRakNetUDT.hpp
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
#include "RakNetDefines.hpp"
|
||||
|
||||
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1
|
||||
|
||||
#ifndef __CONGESTION_CONTROL_UDT_H
|
||||
#define __CONGESTION_CONTROL_UDT_H
|
||||
|
||||
#include "NativeTypes.hpp"
|
||||
#include "RakNetTime.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
|
||||
/// Set to 4 if you are using the iPod Touch TG. See http://www.jenkinssoftware.com/forum/index.php?topic=2717.0
|
||||
#define CC_TIME_TYPE_BYTES 8
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
|
||||
typedef uint64_t CCTimeType;
|
||||
|
||||
|
||||
|
||||
|
||||
typedef uint24_t DatagramSequenceNumberType;
|
||||
typedef double BytesPerMicrosecond;
|
||||
typedef double BytesPerSecond;
|
||||
typedef double MicrosecondsPerByte;
|
||||
|
||||
/// CC_RAKNET_UDT_PACKET_HISTORY_LENGTH should be a power of 2 for the writeIndex variables to wrap properly
|
||||
#define CC_RAKNET_UDT_PACKET_HISTORY_LENGTH 64
|
||||
#define RTT_HISTORY_LENGTH 64
|
||||
|
||||
/// Sizeof an UDP header in byte
|
||||
#define UDP_HEADER_SIZE 28
|
||||
|
||||
#define CC_DEBUG_PRINTF_1(x)
|
||||
#define CC_DEBUG_PRINTF_2(x,y)
|
||||
#define CC_DEBUG_PRINTF_3(x,y,z)
|
||||
#define CC_DEBUG_PRINTF_4(x,y,z,a)
|
||||
#define CC_DEBUG_PRINTF_5(x,y,z,a,b)
|
||||
//#define CC_DEBUG_PRINTF_1(x) printf(x)
|
||||
//#define CC_DEBUG_PRINTF_2(x,y) printf(x,y)
|
||||
//#define CC_DEBUG_PRINTF_3(x,y,z) printf(x,y,z)
|
||||
//#define CC_DEBUG_PRINTF_4(x,y,z,a) printf(x,y,z,a)
|
||||
//#define CC_DEBUG_PRINTF_5(x,y,z,a,b) printf(x,y,z,a,b)
|
||||
|
||||
/// \brief Encapsulates UDT congestion control, as used by RakNet
|
||||
/// Requirements:
|
||||
/// <OL>
|
||||
/// <LI>Each datagram is no more than MAXIMUM_MTU_SIZE, after accounting for the UDP header
|
||||
/// <LI>Each datagram containing a user message has a sequence number which is set after calling OnSendBytes(). Set it by calling GetAndIncrementNextDatagramSequenceNumber()
|
||||
/// <LI>System is designed to be used from a single thread.
|
||||
/// <LI>Each packet should have a timeout time based on GetSenderRTOForACK(). If this time elapses, add the packet to the head of the send list for retransmission.
|
||||
/// </OL>
|
||||
///
|
||||
/// Recommended:
|
||||
/// <OL>
|
||||
/// <LI>Call sendto in its own thread. This takes a significant amount of time in high speed networks.
|
||||
/// </OL>
|
||||
///
|
||||
/// Algorithm:
|
||||
/// <OL>
|
||||
/// <LI>On a new connection, call Init()
|
||||
/// <LI>On a periodic interval (SYN time is the best) call Update(). Also call ShouldSendACKs(), and send buffered ACKS if it returns true.
|
||||
/// <LI>Call OnSendAck() when sending acks.
|
||||
/// <LI>When you want to send or resend data, call GetNumberOfBytesToSend(). It will return you enough bytes to keep you busy for \a estimatedTimeToNextTick. You can send more than this to fill out a datagram, or to send packet pairs
|
||||
/// <LI>Call OnSendBytes() when sending datagrams.
|
||||
/// <LI>When data arrives, record the sequence number and buffer an ACK for it, to be sent from Update() if ShouldSendACKs() returns true
|
||||
/// <LI>Every 16 packets that you send, send two of them back to back (a packet pair) as long as both packets are the same size. If you don't have two packets the same size, it is fine to defer this until you do.
|
||||
/// <LI>When you get a packet, call OnGotPacket(). If the packet is also either of a packet pair, call OnGotPacketPair()
|
||||
/// <LI>If you get a packet, and the sequence number is not 1 + the last sequence number, send a NAK. On the remote system, call OnNAK() and resend that message.
|
||||
/// <LI>If you get an ACK, remove that message from retransmission. Call OnNonDuplicateAck().
|
||||
/// <LI>If a message is not ACKed for GetRTOForRetransmission(), resend it.
|
||||
/// </OL>
|
||||
class CCRakNetUDT
|
||||
{
|
||||
public:
|
||||
|
||||
CCRakNetUDT();
|
||||
~CCRakNetUDT();
|
||||
|
||||
/// Reset all variables to their initial states, for a new connection
|
||||
void Init(CCTimeType curTime, uint32_t maxDatagramPayload);
|
||||
|
||||
/// Update over time
|
||||
void Update(CCTimeType curTime, bool hasDataToSendOrResend);
|
||||
|
||||
int GetRetransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
|
||||
int GetTransmissionBandwidth(CCTimeType curTime, CCTimeType timeSinceLastTick, uint32_t unacknowledgedBytes, bool isContinuousSend);
|
||||
|
||||
/// Acks do not have to be sent immediately. Instead, they can be buffered up such that groups of acks are sent at a time
|
||||
/// This reduces overall bandwidth usage
|
||||
/// How long they can be buffered depends on the retransmit time of the sender
|
||||
/// Should call once per update tick, and send if needed
|
||||
bool ShouldSendACKs(CCTimeType curTime, CCTimeType estimatedTimeToNextTick);
|
||||
|
||||
/// Every data packet sent must contain a sequence number
|
||||
/// Call this function to get it. The sequence number is passed into OnGotPacketPair()
|
||||
DatagramSequenceNumberType GetAndIncrementNextDatagramSequenceNumber(void);
|
||||
DatagramSequenceNumberType GetNextDatagramSequenceNumber(void);
|
||||
|
||||
/// Call this when you send packets
|
||||
/// Every 15th and 16th packets should be sent as a packet pair if possible
|
||||
/// When packets marked as a packet pair arrive, pass to OnGotPacketPair()
|
||||
/// When any packets arrive, (additionally) pass to OnGotPacket
|
||||
/// Packets should contain our system time, so we can pass rtt to OnNonDuplicateAck()
|
||||
void OnSendBytes(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Call this when you get a packet pair
|
||||
void OnGotPacketPair(DatagramSequenceNumberType datagramSequenceNumber, uint32_t sizeInBytes, CCTimeType curTime);
|
||||
|
||||
/// Call this when you get a packet (including packet pairs)
|
||||
/// If the DatagramSequenceNumberType is out of order, skippedMessageCount will be non-zero
|
||||
/// In that case, send a NAK for every sequence number up to that count
|
||||
bool OnGotPacket(DatagramSequenceNumberType datagramSequenceNumber, bool isContinuousSend, CCTimeType curTime, uint32_t sizeInBytes, uint32_t *skippedMessageCount);
|
||||
|
||||
/// Call when you get a NAK, with the sequence number of the lost message
|
||||
/// Affects the congestion control
|
||||
void OnResend(CCTimeType curTime);
|
||||
void OnNAK(CCTimeType curTime, DatagramSequenceNumberType nakSequenceNumber);
|
||||
|
||||
/// Call this when an ACK arrives.
|
||||
/// hasBAndAS are possibly written with the ack, see OnSendAck()
|
||||
/// B and AS are used in the calculations in UpdateWindowSizeAndAckOnAckPerSyn
|
||||
/// B and AS are updated at most once per SYN
|
||||
void OnAck(CCTimeType curTime, CCTimeType rtt, bool hasBAndAS, BytesPerMicrosecond _B, BytesPerMicrosecond _AS, double totalUserDataBytesAcked, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber );
|
||||
void OnDuplicateAck( CCTimeType curTime, DatagramSequenceNumberType sequenceNumber ) {}
|
||||
|
||||
/// Call when you send an ack, to see if the ack should have the B and AS parameters transmitted
|
||||
/// Call before calling OnSendAck()
|
||||
void OnSendAckGetBAndAS(CCTimeType curTime, bool *hasBAndAS, BytesPerMicrosecond *_B, BytesPerMicrosecond *_AS);
|
||||
|
||||
/// Call when we send an ack, to write B and AS if needed
|
||||
/// B and AS are only written once per SYN, to prevent slow calculations
|
||||
/// Also updates SND, the period between sends, since data is written out
|
||||
/// Be sure to call OnSendAckGetBAndAS() before calling OnSendAck(), since whether you write it or not affects \a numBytes
|
||||
void OnSendAck(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Call when we send a NACK
|
||||
/// Also updates SND, the period between sends, since data is written out
|
||||
void OnSendNACK(CCTimeType curTime, uint32_t numBytes);
|
||||
|
||||
/// Retransmission time out for the sender
|
||||
/// If the time difference between when a message was last transmitted, and the current time is greater than RTO then packet is eligible for retransmission, pending congestion control
|
||||
/// RTO = (RTT + 4 * RTTVar) + SYN
|
||||
/// If we have been continuously sending for the last RTO, and no ACK or NAK at all, SND*=2;
|
||||
/// This is per message, which is different from UDT, but RakNet supports packetloss with continuing data where UDT is only RELIABLE_ORDERED
|
||||
/// Minimum value is 100 milliseconds
|
||||
CCTimeType GetRTOForRetransmission(void) const;
|
||||
|
||||
/// Set the maximum amount of data that can be sent in one datagram
|
||||
/// Default to MAXIMUM_MTU_SIZE-UDP_HEADER_SIZE
|
||||
void SetMTU(uint32_t bytes);
|
||||
|
||||
/// Return what was set by SetMTU()
|
||||
uint32_t GetMTU(void) const;
|
||||
|
||||
/// Query for statistics
|
||||
BytesPerMicrosecond GetLocalSendRate(void) const {return 1.0 / SND;}
|
||||
BytesPerMicrosecond GetLocalReceiveRate(CCTimeType currentTime) const;
|
||||
BytesPerMicrosecond GetRemoveReceiveRate(void) const {return AS;}
|
||||
//BytesPerMicrosecond GetEstimatedBandwidth(void) const {return B;}
|
||||
BytesPerMicrosecond GetEstimatedBandwidth(void) const {return GetLinkCapacityBytesPerSecond()*1000000.0;}
|
||||
double GetLinkCapacityBytesPerSecond(void) const {return estimatedLinkCapacityBytesPerSecond;};
|
||||
|
||||
/// Query for statistics
|
||||
double GetRTT(void) const;
|
||||
|
||||
bool GetIsInSlowStart(void) const {return isInSlowStart;}
|
||||
uint32_t GetCWNDLimit(void) const {return (uint32_t) (CWND*MAXIMUM_MTU_INCLUDING_UDP_HEADER);}
|
||||
|
||||
|
||||
/// Is a > b, accounting for variable overflow?
|
||||
static bool GreaterThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
|
||||
/// Is a < b, accounting for variable overflow?
|
||||
static bool LessThan(DatagramSequenceNumberType a, DatagramSequenceNumberType b);
|
||||
// void SetTimeBetweenSendsLimit(unsigned int bitsPerSecond);
|
||||
uint64_t GetBytesPerSecondLimitByCongestionControl(void) const;
|
||||
|
||||
protected:
|
||||
// --------------------------- PROTECTED VARIABLES ---------------------------
|
||||
/// time interval between bytes, in microseconds.
|
||||
/// Only used when slowStart==false
|
||||
/// Increased over time as we continually get messages
|
||||
/// Decreased on NAK and timeout
|
||||
/// Starts at 0 (invalid)
|
||||
MicrosecondsPerByte SND;
|
||||
|
||||
/// Supportive window mechanism, controlling the maximum number of in-flight packets
|
||||
/// Used both during and after slow-start, but primarily during slow-start
|
||||
/// Starts at 2, which is also the low threshhold
|
||||
/// Max is the socket receive buffer / MTU
|
||||
/// CWND = AS * (RTT + SYN) + 16
|
||||
double CWND;
|
||||
|
||||
/// When we do an update process on the SYN interval, nextSYNUpdate is set to the next time we should update
|
||||
/// Normally this is nextSYNUpdate+=SYN, in order to update on a consistent schedule
|
||||
/// However, if this would result in an immediate update yet again, it is set to SYN microseconds past the current time (in case the thread did not update for a long time)
|
||||
CCTimeType nextSYNUpdate;
|
||||
|
||||
|
||||
/// Index into packetPairRecieptHistory where we will next write
|
||||
/// The history is always full (starting with default values) so no read index is needed
|
||||
int packetPairRecieptHistoryWriteIndex;
|
||||
|
||||
/// Sent to the sender by the receiver from packetPairRecieptHistory whenever a back to back packet arrives on the receiver
|
||||
/// Updated by B = B * .875 + incomingB * .125
|
||||
//BytesPerMicrosecond B;
|
||||
|
||||
/// Running round trip time (ping*2)
|
||||
/// Only sender needs to know this
|
||||
/// Initialized to UNSET
|
||||
/// Set to rtt on first calculation
|
||||
/// Updated gradually by RTT = RTT * 0.875 + rtt * 0.125
|
||||
double RTT;
|
||||
|
||||
/// Round trip time variance
|
||||
/// Only sender needs to know this
|
||||
/// Initialized to UNSET
|
||||
/// Set to rtt on first calculation
|
||||
// double RTTVar;
|
||||
/// Update: Use min/max, RTTVar follows current variance too closely resulting in packetloss
|
||||
double minRTT, maxRTT;
|
||||
|
||||
/// Used to calculate packet arrival rate (in UDT) but data arrival rate (in RakNet, where not all datagrams are the same size)
|
||||
/// Filter is used to cull lowest half of values for bytesPerMicrosecond, to discount spikes and inactivity
|
||||
/// Referred to in the documentation as AS, data arrival rate
|
||||
/// AS is sent to the sender and calculated every 10th ack
|
||||
/// Each node represents (curTime-lastPacketArrivalTime)/bytes
|
||||
/// Used with ReceiverCalculateDataArrivalRate();
|
||||
BytesPerMicrosecond packetArrivalHistory[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH];
|
||||
BytesPerMicrosecond packetArrivalHistoryContinuousGaps[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH];
|
||||
unsigned char packetArrivalHistoryContinuousGapsIndex;
|
||||
uint64_t continuousBytesReceived;
|
||||
CCTimeType continuousBytesReceivedStartTime;
|
||||
unsigned int packetArrivalHistoryWriteCount;
|
||||
|
||||
/// Index into packetArrivalHistory where we will next write
|
||||
/// The history is always full (starting with default values) so no read index is needed
|
||||
int packetArrivalHistoryWriteIndex;
|
||||
|
||||
/// Tracks the time the last packet that arrived, so BytesPerMicrosecond can be calculated for packetArrivalHistory when a new packet arrives
|
||||
CCTimeType lastPacketArrivalTime;
|
||||
|
||||
/// Data arrival rate from the sender to the receiver, as told to us by the receiver
|
||||
/// Used to calculate initial sending rate when slow start stops
|
||||
BytesPerMicrosecond AS;
|
||||
|
||||
/// When the receiver last calculated and send B and AS, from packetArrivalHistory and packetPairRecieptHistory
|
||||
/// Used to prevent it from being calculated and send too frequently, as they are slow operations
|
||||
CCTimeType lastTransmitOfBAndAS;
|
||||
|
||||
/// New connections start in slow start
|
||||
/// During slow start, SND is not used, only CWND
|
||||
/// Slow start ends when we get a NAK, or the maximum size of CWND is reached
|
||||
/// SND is initialized to the inverse of the receiver's packet arrival rate when slow start ends
|
||||
bool isInSlowStart;
|
||||
|
||||
/// How many NAKs arrived this congestion period
|
||||
/// Initialized to 1 when the congestion period starts
|
||||
uint32_t NAKCount;
|
||||
|
||||
/// How many NAKs do you get on average during a congestion period?
|
||||
/// Starts at 1
|
||||
/// Used to generate a random number, DecRandom, between 1 and AvgNAKNum
|
||||
uint32_t AvgNAKNum;
|
||||
|
||||
/// How many times we have decremented SND this congestion period. Used to limit the number of decrements to 5
|
||||
uint32_t DecCount;
|
||||
|
||||
/// Every DecInterval NAKs per congestion period, we decrease the send rate
|
||||
uint32_t DecInterval;
|
||||
|
||||
/// Every outgoing datagram is assigned a sequence number, which increments by 1 every assignment
|
||||
DatagramSequenceNumberType nextDatagramSequenceNumber;
|
||||
|
||||
/// If a packet is marked as a packet pair, lastPacketPairPacketArrivalTime is set to the time it arrives
|
||||
/// This is used so when the 2nd packet of the pair arrives, we can calculate the time interval between the two
|
||||
CCTimeType lastPacketPairPacketArrivalTime;
|
||||
|
||||
/// If a packet is marked as a packet pair, lastPacketPairSequenceNumber is checked to see if the last packet we got
|
||||
/// was the packet immediately before the one that arrived
|
||||
/// If so, we can use lastPacketPairPacketArrivalTime to get the time between the two packets, and thus estimate the link capacity
|
||||
/// Initialized to -1, so the first packet of a packet pair won't be treated as the second
|
||||
DatagramSequenceNumberType lastPacketPairSequenceNumber;
|
||||
|
||||
/// Used to cap UpdateWindowSizeAndAckOnAckPerSyn() to once speed increase per SYN
|
||||
/// This is to prevent speeding up faster than congestion control can compensate for
|
||||
CCTimeType lastUpdateWindowSizeAndAck;
|
||||
|
||||
/// Every time SND is halved due to timeout, the RTO is increased
|
||||
/// This is to prevent massive retransmissions to an unresponsive system
|
||||
/// Reset on any data arriving
|
||||
double ExpCount;
|
||||
|
||||
/// Total number of user data bytes sent
|
||||
/// Used to adjust the window size, on ACK, during slow start
|
||||
uint64_t totalUserDataBytesSent;
|
||||
|
||||
/// When we get an ack, if oldestUnsentAck==0, set it to the current time
|
||||
/// When we send out acks, set oldestUnsentAck to 0
|
||||
CCTimeType oldestUnsentAck;
|
||||
|
||||
// Maximum amount of bytes that the user can send, e.g. the size of one full datagram
|
||||
uint32_t MAXIMUM_MTU_INCLUDING_UDP_HEADER;
|
||||
|
||||
// Max window size
|
||||
double CWND_MAX_THRESHOLD;
|
||||
|
||||
/// Track which datagram sequence numbers have arrived.
|
||||
/// If a sequence number is skipped, send a NAK for all skipped messages
|
||||
DatagramSequenceNumberType expectedNextSequenceNumber;
|
||||
|
||||
// How many times have we sent B and AS? Used to force it to send at least CC_RAKNET_UDT_PACKET_HISTORY_LENGTH times
|
||||
// Otherwise, the default values in the array generate inaccuracy
|
||||
uint32_t sendBAndASCount;
|
||||
|
||||
/// Most recent values read into the corresponding lists
|
||||
/// Used during the beginning of a connection, when the median filter is still inaccurate
|
||||
BytesPerMicrosecond mostRecentPacketArrivalHistory;
|
||||
|
||||
bool hasWrittenToPacketPairReceiptHistory;
|
||||
|
||||
// uint32_t rttHistory[RTT_HISTORY_LENGTH];
|
||||
// uint32_t rttHistoryIndex;
|
||||
// uint32_t rttHistoryWriteCount;
|
||||
// uint32_t rttSum, rttLow;
|
||||
// CCTimeType lastSndUpdateTime;
|
||||
double estimatedLinkCapacityBytesPerSecond;
|
||||
|
||||
// --------------------------- PROTECTED METHODS ---------------------------
|
||||
/// Update nextSYNUpdate by SYN, or the same amount past the current time if no updates have occurred for a long time
|
||||
void SetNextSYNUpdate(CCTimeType currentTime);
|
||||
|
||||
/// Returns the rate of data arrival, based on packets arriving on the sender.
|
||||
BytesPerMicrosecond ReceiverCalculateDataArrivalRate(CCTimeType curTime) const;
|
||||
/// Returns the median of the data arrival rate
|
||||
BytesPerMicrosecond ReceiverCalculateDataArrivalRateMedian(void) const;
|
||||
|
||||
/// Calculates the median an array of BytesPerMicrosecond
|
||||
static BytesPerMicrosecond CalculateListMedianRecursive(const BytesPerMicrosecond inputList[CC_RAKNET_UDT_PACKET_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum);
|
||||
// static uint32_t CalculateListMedianRecursive(const uint32_t inputList[RTT_HISTORY_LENGTH], int inputListLength, int lessThanSum, int greaterThanSum);
|
||||
|
||||
/// Same as GetRTOForRetransmission, but does not factor in ExpCount
|
||||
/// This is because the receiver does not know ExpCount for the sender, and even if it did, acks shouldn't be delayed for this reason
|
||||
CCTimeType GetSenderRTOForACK(void) const;
|
||||
|
||||
/// Stop slow start, and enter normal transfer rate
|
||||
void EndSlowStart(void);
|
||||
|
||||
/// Does the named conversion
|
||||
inline double BytesPerMicrosecondToPacketsPerMillisecond(BytesPerMicrosecond in);
|
||||
|
||||
/// Update the round trip time, from ACK or ACK2
|
||||
//void UpdateRTT(CCTimeType rtt);
|
||||
|
||||
/// Update the corresponding variables pre-slow start
|
||||
void UpdateWindowSizeAndAckOnAckPreSlowStart(double totalUserDataBytesAcked);
|
||||
|
||||
/// Update the corresponding variables post-slow start
|
||||
void UpdateWindowSizeAndAckOnAckPerSyn(CCTimeType curTime, CCTimeType rtt, bool isContinuousSend, DatagramSequenceNumberType sequenceNumber);
|
||||
|
||||
|
||||
/// Sets halveSNDOnNoDataTime to the future, and also resets ExpCount, which is used to multiple the RTO on no data arriving at all
|
||||
void ResetOnDataArrivalHalveSNDOnNoDataTime(CCTimeType curTime);
|
||||
|
||||
// Init array
|
||||
void InitPacketArrivalHistory(void);
|
||||
|
||||
// Printf
|
||||
void PrintLowBandwidthWarning(void);
|
||||
|
||||
// Bug: SND can sometimes get super high - have seen 11693
|
||||
void CapMinSnd(const char *file, int line);
|
||||
|
||||
void DecreaseTimeBetweenSends(void);
|
||||
void IncreaseTimeBetweenSends(void);
|
||||
|
||||
int bytesCanSendThisTick;
|
||||
|
||||
CCTimeType lastRttOnIncreaseSendRate;
|
||||
CCTimeType lastRtt;
|
||||
|
||||
DatagramSequenceNumberType nextCongestionControlBlock;
|
||||
bool hadPacketlossThisBlock;
|
||||
DataStructures::Queue<CCTimeType> pingsLastInterval;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
97
third-party/RakNet/src/RakNet/CheckSum.cpp
vendored
Normal file
97
third-party/RakNet/src/RakNet/CheckSum.cpp
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief CheckSum implementation from http://www.flounder.com/checksum.htm
|
||||
*
|
||||
*/
|
||||
#include "CheckSum.hpp"
|
||||
|
||||
/****************************************************************************
|
||||
* CheckSum::add
|
||||
* Inputs:
|
||||
* unsigned int d: word to add
|
||||
* Result: void
|
||||
*
|
||||
* Effect:
|
||||
* Adds the bytes of the unsigned int to the CheckSum
|
||||
****************************************************************************/
|
||||
|
||||
void CheckSum::Add ( unsigned int value )
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned int value;
|
||||
unsigned char bytes[ 4 ];
|
||||
}
|
||||
|
||||
data;
|
||||
data.value = value;
|
||||
|
||||
for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ )
|
||||
Add ( data.bytes[ i ] )
|
||||
|
||||
;
|
||||
} // CheckSum::add(unsigned int)
|
||||
|
||||
/****************************************************************************
|
||||
* CheckSum::add
|
||||
* Inputs:
|
||||
* unsigned short value:
|
||||
* Result: void
|
||||
*
|
||||
* Effect:
|
||||
* Adds the bytes of the unsigned short value to the CheckSum
|
||||
****************************************************************************/
|
||||
|
||||
void CheckSum::Add ( unsigned short value )
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned short value;
|
||||
unsigned char bytes[ 2 ];
|
||||
}
|
||||
|
||||
data;
|
||||
data.value = value;
|
||||
|
||||
for ( unsigned int i = 0; i < sizeof( data.bytes ); i++ )
|
||||
Add ( data.bytes[ i ] )
|
||||
|
||||
;
|
||||
} // CheckSum::add(unsigned short)
|
||||
|
||||
/****************************************************************************
|
||||
* CheckSum::add
|
||||
* Inputs:
|
||||
* unsigned char value:
|
||||
* Result: void
|
||||
*
|
||||
* Effect:
|
||||
* Adds the byte to the CheckSum
|
||||
****************************************************************************/
|
||||
|
||||
void CheckSum::Add ( unsigned char value )
|
||||
{
|
||||
unsigned char cipher = (unsigned char)( value ^ ( r >> 8 ) );
|
||||
r = ( cipher + r ) * c1 + c2;
|
||||
sum += cipher;
|
||||
} // CheckSum::add(unsigned char)
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* CheckSum::add
|
||||
* Inputs:
|
||||
* LPunsigned char b: pointer to byte array
|
||||
* unsigned int length: count
|
||||
* Result: void
|
||||
*
|
||||
* Effect:
|
||||
* Adds the bytes to the CheckSum
|
||||
****************************************************************************/
|
||||
|
||||
void CheckSum::Add ( unsigned char *b, unsigned int length )
|
||||
{
|
||||
for ( unsigned int i = 0; i < length; i++ )
|
||||
Add ( b[ i ] )
|
||||
|
||||
;
|
||||
} // CheckSum::add(LPunsigned char, unsigned int)
|
||||
53
third-party/RakNet/src/RakNet/CheckSum.hpp
vendored
Normal file
53
third-party/RakNet/src/RakNet/CheckSum.hpp
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
///
|
||||
/// \file CheckSum.cpp
|
||||
/// \brief [Internal] CheckSum implementation from http://www.flounder.com/checksum.htm
|
||||
///
|
||||
|
||||
#ifndef __CHECKSUM_H
|
||||
#define __CHECKSUM_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
/// 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
|
||||
242
third-party/RakNet/src/RakNet/CloudClient.cpp
vendored
Normal file
242
third-party/RakNet/src/RakNet/CloudClient.cpp
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_CloudClient==1
|
||||
|
||||
#include "CloudClient.hpp"
|
||||
#include "GetTime.hpp"
|
||||
#include "MessageIdentifiers.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "RakPeerInterface.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(CloudClient,CloudClient);
|
||||
|
||||
CloudClient::CloudClient()
|
||||
{
|
||||
callback=0;
|
||||
allocator=&unsetDefaultAllocator;
|
||||
}
|
||||
CloudClient::~CloudClient()
|
||||
{
|
||||
}
|
||||
void CloudClient::SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback)
|
||||
{
|
||||
callback=_callback;
|
||||
allocator=_allocator;
|
||||
}
|
||||
void CloudClient::Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakAssert(cloudKey);
|
||||
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_POST_REQUEST);
|
||||
cloudKey->Serialize(true,&bsOut);
|
||||
if (data==0)
|
||||
dataLengthBytes=0;
|
||||
bsOut.Write(dataLengthBytes);
|
||||
if (dataLengthBytes>0)
|
||||
bsOut.WriteAlignedBytes((const unsigned char*) data, dataLengthBytes);
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
}
|
||||
void CloudClient::Release(DataStructures::List<CloudKey> &keys, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_RELEASE_REQUEST);
|
||||
RakAssert(keys.Size() < (uint16_t)-1 );
|
||||
bsOut.WriteCasted<uint16_t>(keys.Size());
|
||||
for (uint16_t i=0; i < keys.Size(); i++)
|
||||
{
|
||||
keys[i].Serialize(true,&bsOut);
|
||||
}
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
}
|
||||
bool CloudClient::Get(CloudQuery *keyQuery, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST);
|
||||
keyQuery->Serialize(true, &bsOut);
|
||||
bsOut.WriteCasted<uint16_t>(0); // Specific systems
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
return true;
|
||||
}
|
||||
bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST);
|
||||
keyQuery->Serialize(true, &bsOut);
|
||||
bsOut.WriteCasted<uint16_t>(specificSystems.Size());
|
||||
RakAssert(specificSystems.Size() < (uint16_t)-1 );
|
||||
for (uint16_t i=0; i < specificSystems.Size(); i++)
|
||||
{
|
||||
bsOut.Write(specificSystems[i]);
|
||||
}
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
return true;
|
||||
}
|
||||
bool CloudClient::Get(CloudQuery *keyQuery, DataStructures::List<CloudQueryRow*> &specificSystems, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_GET_REQUEST);
|
||||
keyQuery->Serialize(true, &bsOut);
|
||||
bsOut.WriteCasted<uint16_t>(specificSystems.Size());
|
||||
RakAssert(specificSystems.Size() < (uint16_t)-1 );
|
||||
for (uint16_t i=0; i < specificSystems.Size(); i++)
|
||||
{
|
||||
if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID)
|
||||
{
|
||||
bsOut.Write(true);
|
||||
bsOut.Write(specificSystems[i]->clientGUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
bsOut.Write(false);
|
||||
bsOut.Write(specificSystems[i]->clientSystemAddress);
|
||||
}
|
||||
}
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
return true;
|
||||
}
|
||||
void CloudClient::Unsubscribe(DataStructures::List<CloudKey> &keys, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST);
|
||||
RakAssert(keys.Size() < (uint16_t)-1 );
|
||||
bsOut.WriteCasted<uint16_t>(keys.Size());
|
||||
for (uint16_t i=0; i < keys.Size(); i++)
|
||||
{
|
||||
keys[i].Serialize(true,&bsOut);
|
||||
}
|
||||
bsOut.WriteCasted<uint16_t>(0);
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
}
|
||||
void CloudClient::Unsubscribe(DataStructures::List<CloudKey> &keys, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST);
|
||||
RakAssert(keys.Size() < (uint16_t)-1 );
|
||||
bsOut.WriteCasted<uint16_t>(keys.Size());
|
||||
for (uint16_t i=0; i < keys.Size(); i++)
|
||||
{
|
||||
keys[i].Serialize(true,&bsOut);
|
||||
}
|
||||
bsOut.WriteCasted<uint16_t>(specificSystems.Size());
|
||||
RakAssert(specificSystems.Size() < (uint16_t)-1 );
|
||||
for (uint16_t i=0; i < specificSystems.Size(); i++)
|
||||
{
|
||||
bsOut.Write(specificSystems[i]);
|
||||
}
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
}
|
||||
void CloudClient::Unsubscribe(DataStructures::List<CloudKey> &keys, DataStructures::List<CloudQueryRow*> &specificSystems, RakNetGUID systemIdentifier)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_CLOUD_UNSUBSCRIBE_REQUEST);
|
||||
RakAssert(keys.Size() < (uint16_t)-1 );
|
||||
bsOut.WriteCasted<uint16_t>(keys.Size());
|
||||
for (uint16_t i=0; i < keys.Size(); i++)
|
||||
{
|
||||
keys[i].Serialize(true,&bsOut);
|
||||
}
|
||||
bsOut.WriteCasted<uint16_t>(specificSystems.Size());
|
||||
RakAssert(specificSystems.Size() < (uint16_t)-1 );
|
||||
for (uint16_t i=0; i < specificSystems.Size(); i++)
|
||||
{
|
||||
if (specificSystems[i]->clientGUID!=UNASSIGNED_RAKNET_GUID)
|
||||
{
|
||||
bsOut.Write(true);
|
||||
bsOut.Write(specificSystems[i]->clientGUID);
|
||||
}
|
||||
else
|
||||
{
|
||||
bsOut.Write(false);
|
||||
bsOut.Write(specificSystems[i]->clientSystemAddress);
|
||||
}
|
||||
}
|
||||
SendUnified(&bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, systemIdentifier, false);
|
||||
}
|
||||
PluginReceiveResult CloudClient::OnReceive(Packet *packet)
|
||||
{
|
||||
(void) packet;
|
||||
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
void CloudClient::OnGetReponse(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator)
|
||||
{
|
||||
if (_callback==0)
|
||||
_callback=callback;
|
||||
if (_allocator==0)
|
||||
_allocator=allocator;
|
||||
|
||||
CloudQueryResult cloudQueryResult;
|
||||
|
||||
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
cloudQueryResult.Serialize(false,&bsIn,_allocator);
|
||||
bool deallocateRowsAfterReturn=true;
|
||||
_callback->OnGet(&cloudQueryResult, &deallocateRowsAfterReturn);
|
||||
if (deallocateRowsAfterReturn)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < cloudQueryResult.rowsReturned.Size(); i++)
|
||||
{
|
||||
_allocator->DeallocateRowData(cloudQueryResult.rowsReturned[i]->data);
|
||||
_allocator->DeallocateCloudQueryRow(cloudQueryResult.rowsReturned[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CloudClient::OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator)
|
||||
{
|
||||
if (_allocator==0)
|
||||
_allocator=allocator;
|
||||
|
||||
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
cloudQueryResult->Serialize(false,&bsIn,_allocator);
|
||||
}
|
||||
void CloudClient::OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback, CloudAllocator *_allocator)
|
||||
{
|
||||
if (_callback==0)
|
||||
_callback=callback;
|
||||
if (_allocator==0)
|
||||
_allocator=allocator;
|
||||
|
||||
bool wasUpdated=false;
|
||||
CloudQueryRow row;
|
||||
|
||||
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
bsIn.Read(wasUpdated);
|
||||
row.Serialize(false,&bsIn,_allocator);
|
||||
bool deallocateRowAfterReturn=true;
|
||||
_callback->OnSubscriptionNotification(&row, wasUpdated, &deallocateRowAfterReturn);
|
||||
if (deallocateRowAfterReturn)
|
||||
{
|
||||
_allocator->DeallocateRowData(row.data);
|
||||
}
|
||||
}
|
||||
void CloudClient::OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator)
|
||||
{
|
||||
if (_allocator==0)
|
||||
_allocator=allocator;
|
||||
|
||||
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
bool b=false;
|
||||
bsIn.Read(b);
|
||||
*wasUpdated=b;
|
||||
row->Serialize(false,&bsIn,_allocator);
|
||||
}
|
||||
void CloudClient::DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < cloudQueryResult->rowsReturned.Size(); i++)
|
||||
{
|
||||
allocator->DeallocateRowData(cloudQueryResult->rowsReturned[i]->data);
|
||||
allocator->DeallocateCloudQueryRow(cloudQueryResult->rowsReturned[i]);
|
||||
}
|
||||
}
|
||||
void CloudClient::DeallocateWithDefaultAllocator(CloudQueryRow *row)
|
||||
{
|
||||
allocator->DeallocateRowData(row->data);
|
||||
}
|
||||
#endif
|
||||
163
third-party/RakNet/src/RakNet/CloudClient.hpp
vendored
Normal file
163
third-party/RakNet/src/RakNet/CloudClient.hpp
vendored
Normal file
@@ -0,0 +1,163 @@
|
||||
/// \file CloudClient.h
|
||||
/// \brief Queries CloudMemoryServer to download data that other clients have uploaded
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_CloudClient==1
|
||||
|
||||
#ifndef __CLOUD_CLIENT_H
|
||||
#define __CLOUD_CLIENT_H
|
||||
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "CloudCommon.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_Hash.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
class CloudClientCallback;
|
||||
|
||||
/// \defgroup CLOUD_GROUP CloudComputing
|
||||
/// \brief Contains the CloudClient and CloudServer plugins
|
||||
/// \details The CloudServer plugins operates on requests from the CloudClient plugin. The servers are in a fully connected mesh topology, which the clients are connected to any server. Clients can interact with each other by posting and subscribing to memory updates, without being directly connected or even knowing about each other.
|
||||
/// \ingroup PLUGINS_GROUP
|
||||
|
||||
/// \brief Performs Post() and Get() operations on CloudMemoryServer
|
||||
/// \details A CloudClient is a computer connected to one or more servers in a cloud configuration. Operations by one CloudClient can be received and subscribed to by other instances of CloudClient, without those clients being connected, even on different servers.
|
||||
/// \ingroup CLOUD_GROUP
|
||||
class RAK_DLL_EXPORT CloudClient : public PluginInterface2
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(CloudClient)
|
||||
|
||||
CloudClient();
|
||||
virtual ~CloudClient();
|
||||
|
||||
/// \brief Set the default callbacks for OnGetReponse(), OnSubscriptionNotification(), and OnSubscriptionDataDeleted()
|
||||
/// \details Pointers to CloudAllocator and CloudClientCallback can be stored by the system if desired. If a callback is not provided to OnGetReponse(), OnSubscriptionNotification(), OnSubscriptionDataDeleted(), the callback passed here will be used instead.
|
||||
/// \param[in] _allocator An instance of CloudAllocator
|
||||
/// \param[in] _callback An instance of CloudClientCallback
|
||||
virtual void SetDefaultCallbacks(CloudAllocator *_allocator, CloudClientCallback *_callback);
|
||||
|
||||
/// \brief Uploads data to the cloud
|
||||
/// \details Data uploaded to the cloud will be stored by the server sent to, identified by \a systemIdentifier.
|
||||
/// As long as you are connected to this server, the data will persist. Queries for that data by the Get() operation will
|
||||
/// return the RakNetGUID and SystemAddress of the uploader, as well as the data itself.
|
||||
/// Furthermore, if any clients are subscribed to the particular CloudKey passed, those clients will get update notices that the data has changed
|
||||
/// Passing data with the same \a cloudKey more than once will overwrite the prior value.
|
||||
/// This call will silently fail if CloudServer::SetMaxUploadBytesPerClient() is exceeded
|
||||
/// \param[in] cloudKey Identifies the data being uploaded
|
||||
/// \param[in] data A pointer to data to upload. This pointer does not need to persist past the call
|
||||
/// \param[in] dataLengthBytes The length in bytes of \a data
|
||||
/// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to.
|
||||
virtual void Post(CloudKey *cloudKey, const unsigned char *data, uint32_t dataLengthBytes, RakNetGUID systemIdentifier);
|
||||
|
||||
/// \brief Releases one or more data previously uploaded with Post()
|
||||
/// \details If a remote system has subscribed to one or more of the \a keys uploaded, they will get ID_CLOUD_SUBSCRIPTION_NOTIFICATION notifications containing the last value uploaded before deletions
|
||||
/// \param[in] cloudKey Identifies the data to release. It is possible to remove uploads from multiple Post() calls at once.
|
||||
/// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to.
|
||||
virtual void Release(DataStructures::List<CloudKey> &keys, RakNetGUID systemIdentifier);
|
||||
|
||||
/// \brief Gets data from the cloud
|
||||
/// \details For a given query containing one or more keys, return data that matches those keys.
|
||||
/// The values will be returned in the ID_CLOUD_GET_RESPONSE packet, which should be passed to OnGetReponse() and will invoke CloudClientCallback::OnGet()
|
||||
/// CloudQuery::startingRowIndex is used to skip the first n values that would normally be returned..
|
||||
/// CloudQuery::maxRowsToReturn is used to limit the number of rows returned. The number of rows returned may also be limited by CloudServer::SetMaxBytesPerDownload();
|
||||
/// CloudQuery::subscribeToResults if set to true, will cause ID_CLOUD_SUBSCRIPTION_NOTIFICATION to be returned to us when any of the keys in the query are updated or are deleted.
|
||||
/// ID_CLOUD_GET_RESPONSE will be returned even if subscribing to the result list. Only later updates will return ID_CLOUD_SUBSCRIPTION_NOTIFICATION.
|
||||
/// Calling Get() with CloudQuery::subscribeToResults false, when you are already subscribed, does not remove the subscription. Use Unsubscribe() for this.
|
||||
/// Resubscribing using the same CloudKey but a different or no \a specificSystems overwrites the subscribed systems for those keys.
|
||||
/// \param[in] cloudQuery One or more keys, and optional parameters to perform with the Get
|
||||
/// \param[in] systemIdentifier A remote system running CloudServer that we are already connected to.
|
||||
/// \param[in] specificSystems It is possible to get or subscribe to updates only for specific uploading CloudClient instances. Pass the desired instances here. The overload that does not have the specificSystems parameter is treated as subscribing to all updates from all clients.
|
||||
virtual bool Get(CloudQuery *cloudQuery, RakNetGUID systemIdentifier);
|
||||
virtual bool Get(CloudQuery *cloudQuery, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier);
|
||||
virtual bool Get(CloudQuery *cloudQuery, DataStructures::List<CloudQueryRow*> &specificSystems, RakNetGUID systemIdentifier);
|
||||
|
||||
/// \brief Unsubscribe from updates previously subscribed to using Get() with the CloudQuery::subscribeToResults set to true
|
||||
/// The \a keys and \a specificSystems parameters are logically treated as AND when checking subscriptions on the server
|
||||
/// The overload that does not take specificSystems unsubscribes to all passed keys, regardless of system
|
||||
/// You cannot unsubscribe specific systems when previously subscribed to updates from any system. To do this, first Unsubscribe() from all systems, and call Get() with the \a specificSystems parameter explicilty listing the systems you want to subscribe to.
|
||||
virtual void Unsubscribe(DataStructures::List<CloudKey> &keys, RakNetGUID systemIdentifier);
|
||||
virtual void Unsubscribe(DataStructures::List<CloudKey> &keys, DataStructures::List<RakNetGUID> &specificSystems, RakNetGUID systemIdentifier);
|
||||
virtual void Unsubscribe(DataStructures::List<CloudKey> &keys, DataStructures::List<CloudQueryRow*> &specificSystems, RakNetGUID systemIdentifier);
|
||||
|
||||
/// \brief Call this when you get ID_CLOUD_GET_RESPONSE
|
||||
/// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used
|
||||
/// \param[in] packet Packet structure returned from RakPeerInterface
|
||||
/// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used.
|
||||
/// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used.
|
||||
virtual void OnGetReponse(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0);
|
||||
|
||||
/// \brief Call this when you get ID_CLOUD_GET_RESPONSE
|
||||
/// Different form of OnGetReponse that returns to a structure that you pass, instead of using a callback
|
||||
/// You are responsible for deallocation with this form
|
||||
/// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used
|
||||
/// \param[out] cloudQueryResult A pointer to a structure that will be filled out with data
|
||||
/// \param[in] packet Packet structure returned from RakPeerInterface
|
||||
/// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used.
|
||||
virtual void OnGetReponse(CloudQueryResult *cloudQueryResult, Packet *packet, CloudAllocator *_allocator=0);
|
||||
|
||||
/// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION
|
||||
/// If \a callback or \a allocator are 0, the default callbacks passed to SetDefaultCallbacks() are used
|
||||
/// \param[in] packet Packet structure returned from RakPeerInterface
|
||||
/// \param[in] _callback Callback to be called from the function containing output parameters. If 0, default is used.
|
||||
/// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used.
|
||||
virtual void OnSubscriptionNotification(Packet *packet, CloudClientCallback *_callback=0, CloudAllocator *_allocator=0);
|
||||
|
||||
/// \brief Call this when you get ID_CLOUD_SUBSCRIPTION_NOTIFICATION
|
||||
/// Different form of OnSubscriptionNotification that returns to a structure that you pass, instead of using a callback
|
||||
/// You are responsible for deallocation with this form
|
||||
/// If \a allocator is 0, the default callback passed to SetDefaultCallbacks() are used
|
||||
/// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion
|
||||
/// \param[out] row A pointer to a structure that will be filled out with data
|
||||
/// \param[in] packet Packet structure returned from RakPeerInterface
|
||||
/// \param[in] _allocator Allocator to be used to allocate data. If 0, default is used.
|
||||
virtual void OnSubscriptionNotification(bool *wasUpdated, CloudQueryRow *row, Packet *packet, CloudAllocator *_allocator=0);
|
||||
|
||||
/// If you never specified an allocator, and used the non-callback form of OnGetReponse(), deallocate cloudQueryResult with this function
|
||||
virtual void DeallocateWithDefaultAllocator(CloudQueryResult *cloudQueryResult);
|
||||
|
||||
/// If you never specified an allocator, and used the non-callback form of OnSubscriptionNotification(), deallocate row with this function
|
||||
virtual void DeallocateWithDefaultAllocator(CloudQueryRow *row);
|
||||
|
||||
protected:
|
||||
PluginReceiveResult OnReceive(Packet *packet);
|
||||
|
||||
CloudClientCallback *callback;
|
||||
CloudAllocator *allocator;
|
||||
|
||||
CloudAllocator unsetDefaultAllocator;
|
||||
};
|
||||
|
||||
/// \ingroup CLOUD_GROUP
|
||||
/// Parses ID_CLOUD_GET_RESPONSE and ID_CLOUD_SUBSCRIPTION_NOTIFICATION in a convenient callback form
|
||||
class RAK_DLL_EXPORT CloudClientCallback
|
||||
{
|
||||
public:
|
||||
CloudClientCallback() {}
|
||||
virtual ~CloudClientCallback() {}
|
||||
|
||||
/// \brief Called in response to ID_CLOUD_GET_RESPONSE
|
||||
/// \param[out] result Contains the original query passed to Get(), and a list of rows returned.
|
||||
/// \param[out] deallocateRowsAfterReturn CloudQueryResult::rowsReturned will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator.
|
||||
virtual void OnGet(RakNet::CloudQueryResult *result, bool *deallocateRowsAfterReturn) {(void) result; (void) deallocateRowsAfterReturn;}
|
||||
|
||||
/// \brief Called in response to ID_CLOUD_SUBSCRIPTION_NOTIFICATION
|
||||
/// \param[out] result Contains the row updated
|
||||
/// \param[out] wasUpdated If true, the row was updated. If false, it was deleted. \a result will contain the last value just before deletion
|
||||
/// \param[out] deallocateRowAfterReturn \a result will be deallocated after the function returns by default. Set to false to not deallocate these pointers. The pointers are allocated through CloudAllocator.
|
||||
virtual void OnSubscriptionNotification(RakNet::CloudQueryRow *result, bool wasUpdated, bool *deallocateRowAfterReturn) {(void) result; (void) wasUpdated; (void) deallocateRowAfterReturn;}
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
159
third-party/RakNet/src/RakNet/CloudCommon.cpp
vendored
Normal file
159
third-party/RakNet/src/RakNet/CloudCommon.cpp
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1
|
||||
|
||||
#include "CloudCommon.hpp"
|
||||
#include "BitStream.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
int RakNet::CloudKeyComp(const CloudKey &key, const CloudKey &data)
|
||||
{
|
||||
if (key.primaryKey < data.primaryKey)
|
||||
return -1;
|
||||
if (key.primaryKey > data.primaryKey)
|
||||
return 1;
|
||||
if (key.secondaryKey < data.secondaryKey)
|
||||
return -1;
|
||||
if (key.secondaryKey > data.secondaryKey)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
CloudQueryRow* CloudAllocator::AllocateCloudQueryRow(void)
|
||||
{
|
||||
return RakNet::OP_NEW<CloudQueryRow>(_FILE_AND_LINE_);
|
||||
}
|
||||
void CloudAllocator::DeallocateCloudQueryRow(CloudQueryRow *row)
|
||||
{
|
||||
RakNet::OP_DELETE(row,_FILE_AND_LINE_);
|
||||
}
|
||||
unsigned char *CloudAllocator::AllocateRowData(uint32_t bytesNeededForData)
|
||||
{
|
||||
return (unsigned char*) rakMalloc_Ex(bytesNeededForData,_FILE_AND_LINE_);
|
||||
}
|
||||
void CloudAllocator::DeallocateRowData(void *data)
|
||||
{
|
||||
rakFree_Ex(data, _FILE_AND_LINE_);
|
||||
}
|
||||
void CloudKey::Serialize(bool writeToBitstream, BitStream *bitStream)
|
||||
{
|
||||
bitStream->Serialize(writeToBitstream, primaryKey);
|
||||
bitStream->Serialize(writeToBitstream, secondaryKey);
|
||||
}
|
||||
void CloudQuery::Serialize(bool writeToBitstream, BitStream *bitStream)
|
||||
{
|
||||
bool startingRowIndexIsZero=0;
|
||||
bool maxRowsToReturnIsZero=0;
|
||||
startingRowIndexIsZero=startingRowIndex==0;
|
||||
maxRowsToReturnIsZero=maxRowsToReturn==0;
|
||||
bitStream->Serialize(writeToBitstream,startingRowIndexIsZero);
|
||||
bitStream->Serialize(writeToBitstream,maxRowsToReturnIsZero);
|
||||
bitStream->Serialize(writeToBitstream,subscribeToResults);
|
||||
if (startingRowIndexIsZero==false)
|
||||
bitStream->Serialize(writeToBitstream,startingRowIndex);
|
||||
if (maxRowsToReturnIsZero==false)
|
||||
bitStream->Serialize(writeToBitstream,maxRowsToReturn);
|
||||
RakAssert(keys.Size()<(uint16_t)-1);
|
||||
uint16_t numKeys = (uint16_t) keys.Size();
|
||||
bitStream->Serialize(writeToBitstream,numKeys);
|
||||
if (writeToBitstream)
|
||||
{
|
||||
for (uint16_t i=0; i < numKeys; i++)
|
||||
{
|
||||
keys[i].Serialize(true,bitStream);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloudKey cmdk;
|
||||
for (uint16_t i=0; i < numKeys; i++)
|
||||
{
|
||||
cmdk.Serialize(false,bitStream);
|
||||
keys.Push(cmdk, _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
}
|
||||
void CloudQueryRow::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator)
|
||||
{
|
||||
key.Serialize(writeToBitstream,bitStream);
|
||||
bitStream->Serialize(writeToBitstream,serverSystemAddress);
|
||||
bitStream->Serialize(writeToBitstream,clientSystemAddress);
|
||||
bitStream->Serialize(writeToBitstream,serverGUID);
|
||||
bitStream->Serialize(writeToBitstream,clientGUID);
|
||||
bitStream->Serialize(writeToBitstream,length);
|
||||
if (writeToBitstream)
|
||||
{
|
||||
bitStream->WriteAlignedBytes((const unsigned char*) data,length);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (length>0)
|
||||
{
|
||||
data = allocator->AllocateRowData(length);
|
||||
if (data)
|
||||
{
|
||||
bitStream->ReadAlignedBytes((unsigned char *) data,length);
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyOutOfMemory(_FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
else
|
||||
data=0;
|
||||
}
|
||||
}
|
||||
void CloudQueryResult::SerializeHeader(bool writeToBitstream, BitStream *bitStream)
|
||||
{
|
||||
cloudQuery.Serialize(writeToBitstream,bitStream);
|
||||
bitStream->Serialize(writeToBitstream,subscribeToResults);
|
||||
}
|
||||
void CloudQueryResult::SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream)
|
||||
{
|
||||
bitStream->Serialize(writeToBitstream,numRows);
|
||||
}
|
||||
void CloudQueryResult::SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator)
|
||||
{
|
||||
if (writeToBitstream)
|
||||
{
|
||||
for (uint16_t i=0; i < numRows; i++)
|
||||
{
|
||||
rowsReturned[i]->Serialize(true,bitStream, allocator);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloudQueryRow* cmdr;
|
||||
for (uint16_t i=0; i < numRows; i++)
|
||||
{
|
||||
cmdr = allocator->AllocateCloudQueryRow();
|
||||
if (cmdr)
|
||||
{
|
||||
cmdr->Serialize(false,bitStream,allocator);
|
||||
if (cmdr->data==0 && cmdr->length>0)
|
||||
{
|
||||
allocator->DeallocateCloudQueryRow(cmdr);
|
||||
notifyOutOfMemory(_FILE_AND_LINE_);
|
||||
numRows=i;
|
||||
return;
|
||||
}
|
||||
rowsReturned.Push(cmdr, _FILE_AND_LINE_);
|
||||
}
|
||||
else
|
||||
{
|
||||
notifyOutOfMemory(_FILE_AND_LINE_);
|
||||
numRows=i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void CloudQueryResult::Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator)
|
||||
{
|
||||
SerializeHeader(writeToBitstream, bitStream);
|
||||
uint32_t numRows = (uint32_t) rowsReturned.Size();
|
||||
SerializeNumRows(writeToBitstream, numRows, bitStream);
|
||||
SerializeCloudQueryRows(writeToBitstream, numRows, bitStream, allocator);
|
||||
}
|
||||
|
||||
#endif // #if _RAKNET_SUPPORT_CloudMemoryClient==1 || _RAKNET_SUPPORT_CloudMemoryServer==1
|
||||
141
third-party/RakNet/src/RakNet/CloudCommon.hpp
vendored
Normal file
141
third-party/RakNet/src/RakNet/CloudCommon.hpp
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1
|
||||
|
||||
#ifndef __CLOUD_COMMON_H
|
||||
#define __CLOUD_COMMON_H
|
||||
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "RakString.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
class BitStream;
|
||||
struct CloudQueryRow;
|
||||
|
||||
/// Allocates CloudQueryRow and the row data. Override to use derived classes or different allocators
|
||||
/// \ingroup CLOUD_GROUP
|
||||
class RAK_DLL_EXPORT CloudAllocator
|
||||
{
|
||||
public:
|
||||
CloudAllocator() {}
|
||||
virtual ~CloudAllocator() {}
|
||||
|
||||
/// \brief Allocate a row
|
||||
virtual CloudQueryRow* AllocateCloudQueryRow(void);
|
||||
/// \brief Free a row
|
||||
virtual void DeallocateCloudQueryRow(CloudQueryRow *row);
|
||||
/// \brief Allocate CloudQueryRow::data
|
||||
virtual unsigned char *AllocateRowData(uint32_t bytesNeededForData);
|
||||
/// \brief Free CloudQueryRow::data
|
||||
virtual void DeallocateRowData(void *data);
|
||||
};
|
||||
|
||||
/// Serves as a key to identify data uploaded to or queried from the server.
|
||||
/// \ingroup CLOUD_GROUP
|
||||
struct RAK_DLL_EXPORT CloudKey
|
||||
{
|
||||
CloudKey() {}
|
||||
CloudKey(RakNet::RakString _primaryKey, uint32_t _secondaryKey) : primaryKey(_primaryKey), secondaryKey(_secondaryKey) {}
|
||||
~CloudKey() {}
|
||||
|
||||
/// Identifies the primary key. This is intended to be a major category, such as the name of the application
|
||||
/// Must be non-empty
|
||||
RakNet::RakString primaryKey;
|
||||
|
||||
/// Identifies the secondary key. This is intended to be a subcategory enumeration, such as PLAYER_LIST or RUNNING_SCORES
|
||||
uint32_t secondaryKey;
|
||||
|
||||
/// \internal
|
||||
void Serialize(bool writeToBitstream, BitStream *bitStream);
|
||||
};
|
||||
|
||||
/// \internal
|
||||
int CloudKeyComp(const CloudKey &key, const CloudKey &data);
|
||||
|
||||
/// Data members used to query the cloud
|
||||
/// \ingroup CLOUD_GROUP
|
||||
struct RAK_DLL_EXPORT CloudQuery
|
||||
{
|
||||
CloudQuery() {startingRowIndex=0; maxRowsToReturn=0; subscribeToResults=false;}
|
||||
|
||||
/// List of keys to query. Must be at least of length 1.
|
||||
/// This query is run on uploads from all clients, and those that match the combination of primaryKey and secondaryKey are potentially returned
|
||||
/// If you pass more than one key at a time, the results are concatenated so if you need to differentiate between queries then send two different queries
|
||||
DataStructures::List<CloudKey> keys;
|
||||
|
||||
/// If limiting the number of rows to return, this is the starting offset into the list. Has no effect unless maxRowsToReturn is > 0
|
||||
uint32_t startingRowIndex;
|
||||
|
||||
/// Maximum number of rows to return. Actual number may still be less than this. Pass 0 to mean no-limit.
|
||||
uint32_t maxRowsToReturn;
|
||||
|
||||
/// If true, automatically get updates as the results returned to you change. Unsubscribe with CloudMemoryClient::Unsubscribe()
|
||||
bool subscribeToResults;
|
||||
|
||||
/// \internal
|
||||
void Serialize(bool writeToBitstream, BitStream *bitStream);
|
||||
};
|
||||
|
||||
/// \ingroup CLOUD_GROUP
|
||||
struct RAK_DLL_EXPORT CloudQueryRow
|
||||
{
|
||||
/// Key used to identify this data
|
||||
CloudKey key;
|
||||
|
||||
/// Data uploaded
|
||||
unsigned char *data;
|
||||
|
||||
/// Length of data uploaded
|
||||
uint32_t length;
|
||||
|
||||
/// System address of server that is holding this data, and the client is connected to
|
||||
SystemAddress serverSystemAddress;
|
||||
|
||||
/// System address of client that uploaded this data
|
||||
SystemAddress clientSystemAddress;
|
||||
|
||||
/// RakNetGUID of server that is holding this data, and the client is connected to
|
||||
RakNetGUID serverGUID;
|
||||
|
||||
/// RakNetGUID of client that uploaded this data
|
||||
RakNetGUID clientGUID;
|
||||
|
||||
/// \internal
|
||||
void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator);
|
||||
};
|
||||
|
||||
/// \ingroup CLOUD_GROUP
|
||||
struct RAK_DLL_EXPORT CloudQueryResult
|
||||
{
|
||||
/// Query originally passed to Download()
|
||||
CloudQuery cloudQuery;
|
||||
|
||||
/// Results returned from query. If there were multiple keys in CloudQuery::keys then see resultKeyIndices
|
||||
DataStructures::List<CloudQueryRow*> rowsReturned;
|
||||
|
||||
/// If there were multiple keys in CloudQuery::keys, then each key is processed in order and the result concatenated to rowsReturned
|
||||
/// The starting index of each query is written to resultKeyIndices
|
||||
/// For example, if CloudQuery::keys had 4 keys, returning 3 rows, 0, rows, 5 rows, and 12 rows then
|
||||
/// resultKeyIndices would be 0, 3, 3, 8
|
||||
DataStructures::List<uint32_t> resultKeyIndices;
|
||||
|
||||
|
||||
/// Whatever was passed to CloudClient::Get() as CloudQuery::subscribeToResults
|
||||
bool subscribeToResults;
|
||||
|
||||
/// \internal
|
||||
void Serialize(bool writeToBitstream, BitStream *bitStream, CloudAllocator *allocator);
|
||||
/// \internal
|
||||
void SerializeHeader(bool writeToBitstream, BitStream *bitStream);
|
||||
/// \internal
|
||||
void SerializeNumRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream);
|
||||
/// \internal
|
||||
void SerializeCloudQueryRows(bool writeToBitstream, uint32_t &numRows, BitStream *bitStream, CloudAllocator *allocator);
|
||||
};
|
||||
|
||||
} // Namespace RakNet
|
||||
|
||||
#endif // __CLOUD_COMMON_H
|
||||
|
||||
#endif // #if _RAKNET_SUPPORT_CloudClient==1 || _RAKNET_SUPPORT_CloudServer==1
|
||||
1673
third-party/RakNet/src/RakNet/CloudServer.cpp
vendored
Normal file
1673
third-party/RakNet/src/RakNet/CloudServer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
375
third-party/RakNet/src/RakNet/CloudServer.hpp
vendored
Normal file
375
third-party/RakNet/src/RakNet/CloudServer.hpp
vendored
Normal file
@@ -0,0 +1,375 @@
|
||||
/// \file CloudServer.h
|
||||
/// \brief Stores client data, and allows cross-server communication to retrieve this data
|
||||
/// \details TODO
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_CloudServer==1
|
||||
|
||||
#ifndef __CLOUD_SERVER_H
|
||||
#define __CLOUD_SERVER_H
|
||||
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "NativeTypes.hpp"
|
||||
#include "RakString.hpp"
|
||||
#include "DS_Hash.hpp"
|
||||
#include "CloudCommon.hpp"
|
||||
#include "DS_OrderedList.hpp"
|
||||
|
||||
/// If the data is smaller than this value, an allocation is avoid. However, this value exists for every row
|
||||
#define CLOUD_SERVER_DATA_STACK_SIZE 32
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
|
||||
/// \brief Zero or more instances of CloudServerQueryFilter can be attached to CloudServer to restrict client queries
|
||||
/// All attached instances of CloudServerQueryFilter on each corresponding operation, from all directly connected clients
|
||||
/// If any attached instance returns false for a given operation, that operation is silently rejected
|
||||
/// \ingroup CLOUD_GROUP
|
||||
class RAK_DLL_EXPORT CloudServerQueryFilter
|
||||
{
|
||||
public:
|
||||
CloudServerQueryFilter() {}
|
||||
virtual ~CloudServerQueryFilter() {}
|
||||
|
||||
/// Called when a local client wants to post data
|
||||
/// \return true to allow, false to reject
|
||||
virtual bool OnPostRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudKey key, uint32_t dataLength, const char *data)=0;
|
||||
|
||||
/// Called when a local client wants to release data that it has previously uploaded
|
||||
/// \return true to allow, false to reject
|
||||
virtual bool OnReleaseRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List<CloudKey> &cloudKeys)=0;
|
||||
|
||||
/// Called when a local client wants to query data
|
||||
/// If you return false, the client will get no response at all
|
||||
/// \return true to allow, false to reject
|
||||
virtual bool OnGetRequest(RakNetGUID clientGuid, SystemAddress clientAddress, CloudQuery &query, DataStructures::List<RakNetGUID> &specificSystems)=0;
|
||||
|
||||
/// Called when a local client wants to stop getting updates for data
|
||||
/// If you return false, the client will keep getting updates for that data
|
||||
/// \return true to allow, false to reject
|
||||
virtual bool OnUnsubscribeRequest(RakNetGUID clientGuid, SystemAddress clientAddress, DataStructures::List<CloudKey> &cloudKeys, DataStructures::List<RakNetGUID> &specificSystems)=0;
|
||||
};
|
||||
|
||||
/// \brief Stores client data, and allows cross-server communication to retrieve this data
|
||||
/// \ingroup CLOUD_GROUP
|
||||
class RAK_DLL_EXPORT CloudServer : public PluginInterface2, CloudAllocator
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(CloudServer)
|
||||
|
||||
CloudServer();
|
||||
virtual ~CloudServer();
|
||||
|
||||
/// \brief Max bytes a client can upload
|
||||
/// Data in excess of this value is silently ignored
|
||||
/// defaults to 0 (unlimited)
|
||||
/// \param[in] bytes Max bytes a client can upload. 0 means unlimited.
|
||||
void SetMaxUploadBytesPerClient(uint64_t bytes);
|
||||
|
||||
/// \brief Max bytes returned by a download. If the number of bytes would exceed this amount, the returned list is truncated
|
||||
/// However, if this would result in no rows downloaded, then one row will be returned.
|
||||
/// \param[in] bytes Max bytes a client can download from a single Get(). 0 means unlimited.
|
||||
void SetMaxBytesPerDownload(uint64_t bytes);
|
||||
|
||||
/// \brief Add a server, which is assumed to be connected in a fully connected mesh to all other servers and also running the CloudServer plugin
|
||||
/// The other system must also call AddServer before getting the subscription data, or it will be rejected.
|
||||
/// Sending a message telling the other system to call AddServer(), followed by calling AddServer() locally, would be sufficient for this to work.
|
||||
/// \note This sends subscription data to the other system, using RELIABLE_ORDERED on channel 0
|
||||
/// \param[in] systemIdentifier Identifier of the remote system
|
||||
void AddServer(RakNetGUID systemIdentifier);
|
||||
|
||||
/// \brief Removes a server added through AddServer()
|
||||
/// \param[in] systemIdentifier Identifier of the remote system
|
||||
void RemoveServer(RakNetGUID systemIdentifier);
|
||||
|
||||
/// Return list of servers added with AddServer()
|
||||
/// \param[out] remoteServers List of servers added
|
||||
void GetRemoteServers(DataStructures::List<RakNetGUID> &remoteServersOut);
|
||||
|
||||
/// \brief Frees all memory. Does not remove query filters
|
||||
void Clear(void);
|
||||
|
||||
/// \brief Report the specified SystemAddress to client queries, rather than what RakPeer reads.
|
||||
/// This is useful if you already know your public IP
|
||||
/// This only applies to future updates, so call it before updating to apply to all queries
|
||||
/// \param[in] forcedAddress The systmeAddress to return in queries. Use UNASSIGNED_SYSTEM_ADDRESS (default) to use what RakPeer returns
|
||||
void ForceExternalSystemAddress(SystemAddress forcedAddress);
|
||||
|
||||
/// \brief Adds a callback called on each query. If all filters returns true for an operation, the operation is allowed.
|
||||
/// If the filter was already added, the function silently fails
|
||||
/// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters()
|
||||
void AddQueryFilter(CloudServerQueryFilter* filter);
|
||||
|
||||
/// \brief Removes a callback added with AddQueryFilter()
|
||||
/// The instance is not deleted, only unreferenced. It is up to the user to delete the instance, if necessary
|
||||
/// \param[in] filter An externally allocated instance of CloudServerQueryFilter. The instance must remain valid until it is removed with RemoveQueryFilter() or RemoveAllQueryFilters()
|
||||
void RemoveQueryFilter(CloudServerQueryFilter* filter);
|
||||
|
||||
/// \brief Removes all instances of CloudServerQueryFilter added with AddQueryFilter().
|
||||
/// The instances are not deleted, only unreferenced. It is up to the user to delete the instances, if necessary
|
||||
void RemoveAllQueryFilters(void);
|
||||
|
||||
protected:
|
||||
virtual void Update(void);
|
||||
virtual PluginReceiveResult OnReceive(Packet *packet);
|
||||
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
|
||||
virtual void OnRakPeerShutdown(void);
|
||||
|
||||
|
||||
virtual void OnPostRequest(Packet *packet);
|
||||
virtual void OnReleaseRequest(Packet *packet);
|
||||
virtual void OnGetRequest(Packet *packet);
|
||||
virtual void OnUnsubscribeRequest(Packet *packet);
|
||||
virtual void OnServerToServerGetRequest(Packet *packet);
|
||||
virtual void OnServerToServerGetResponse(Packet *packet);
|
||||
|
||||
uint64_t maxUploadBytesPerClient, maxBytesPerDowload;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// For a given data key, quickly look up one or all systems that have uploaded
|
||||
// ----------------------------------------------------------------------------
|
||||
struct CloudData
|
||||
{
|
||||
CloudData() {}
|
||||
~CloudData() {if (allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_);}
|
||||
bool IsUnused(void) const {return isUploaded==false && specificSubscribers.Size()==0;}
|
||||
void Clear(void) {if (dataPtr==allocatedData) rakFree_Ex(allocatedData, _FILE_AND_LINE_); allocatedData=0; dataPtr=0; dataLengthBytes=0; isUploaded=false;}
|
||||
|
||||
unsigned char stackData[CLOUD_SERVER_DATA_STACK_SIZE];
|
||||
unsigned char *allocatedData; // Uses allocatedData instead of stackData if length of data exceeds CLOUD_SERVER_DATA_STACK_SIZE
|
||||
unsigned char *dataPtr; // Points to either stackData or allocatedData
|
||||
uint32_t dataLengthBytes;
|
||||
bool isUploaded;
|
||||
|
||||
/// System address of server that is holding this data, and the client is connected to
|
||||
SystemAddress serverSystemAddress;
|
||||
|
||||
/// System address of client that uploaded this data
|
||||
SystemAddress clientSystemAddress;
|
||||
|
||||
/// RakNetGUID of server that is holding this data, and the client is connected to
|
||||
RakNetGUID serverGUID;
|
||||
|
||||
/// RakNetGUID of client that uploaded this data
|
||||
RakNetGUID clientGUID;
|
||||
|
||||
/// When the key data changes from this particular system, notify these subscribers
|
||||
/// This list mutually exclusive with CloudDataList::nonSpecificSubscribers
|
||||
DataStructures::OrderedList<RakNetGUID, RakNetGUID> specificSubscribers;
|
||||
};
|
||||
void WriteCloudQueryRowFromResultList(unsigned int i, DataStructures::List<CloudData*> &cloudDataResultList, DataStructures::List<CloudKey> &cloudKeyResultList, BitStream *bsOut);
|
||||
void WriteCloudQueryRowFromResultList(DataStructures::List<CloudData*> &cloudDataResultList, DataStructures::List<CloudKey> &cloudKeyResultList, BitStream *bsOut);
|
||||
|
||||
static int KeyDataPtrComp( const RakNetGUID &key, CloudData* const &data );
|
||||
struct CloudDataList
|
||||
{
|
||||
bool IsUnused(void) const {return keyData.Size()==0 && nonSpecificSubscribers.Size()==0;}
|
||||
bool IsNotUploaded(void) const {return uploaderCount==0;}
|
||||
bool RemoveSubscriber(RakNetGUID g) {
|
||||
bool objectExists;
|
||||
unsigned int index;
|
||||
index = nonSpecificSubscribers.GetIndexFromKey(g, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
subscriberCount--;
|
||||
nonSpecificSubscribers.RemoveAtIndex(index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int uploaderCount, subscriberCount;
|
||||
CloudKey key;
|
||||
|
||||
// Data uploaded from or subscribed to for various systems
|
||||
DataStructures::OrderedList<RakNetGUID, CloudData*, CloudServer::KeyDataPtrComp> keyData;
|
||||
|
||||
/// When the key data changes from any system, notify these subscribers
|
||||
/// This list mutually exclusive with CloudData::specificSubscribers
|
||||
DataStructures::OrderedList<RakNetGUID, RakNetGUID> nonSpecificSubscribers;
|
||||
};
|
||||
|
||||
static int KeyDataListComp( const CloudKey &key, CloudDataList * const &data );
|
||||
DataStructures::OrderedList<CloudKey, CloudDataList*, CloudServer::KeyDataListComp> dataRepository;
|
||||
|
||||
struct KeySubscriberID
|
||||
{
|
||||
CloudKey key;
|
||||
DataStructures::OrderedList<RakNetGUID, RakNetGUID> specificSystemsSubscribedTo;
|
||||
};
|
||||
static int KeySubscriberIDComp(const CloudKey &key, KeySubscriberID * const &data );
|
||||
|
||||
// Remote systems
|
||||
struct RemoteCloudClient
|
||||
{
|
||||
bool IsUnused(void) const {return uploadedKeys.Size()==0 && subscribedKeys.Size()==0;}
|
||||
|
||||
DataStructures::OrderedList<CloudKey,CloudKey,CloudKeyComp> uploadedKeys;
|
||||
DataStructures::OrderedList<CloudKey,KeySubscriberID*,CloudServer::KeySubscriberIDComp> subscribedKeys;
|
||||
uint64_t uploadedBytes;
|
||||
};
|
||||
DataStructures::Hash<RakNetGUID, RemoteCloudClient*, 2048, RakNetGUID::ToUint32> remoteSystems;
|
||||
|
||||
// For a given user, release all subscribed and uploaded keys
|
||||
void ReleaseSystem(RakNetGUID clientAddress );
|
||||
|
||||
// For a given user, release a set of keys
|
||||
void ReleaseKeys(RakNetGUID clientAddress, DataStructures::List<CloudKey> &keys );
|
||||
|
||||
void NotifyClientSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, DataStructures::OrderedList<RakNetGUID, RakNetGUID> &subscribers, bool wasUpdated );
|
||||
void NotifyClientSubscribersOfDataChange( CloudQueryRow *row, DataStructures::OrderedList<RakNetGUID, RakNetGUID> &subscribers, bool wasUpdated );
|
||||
void NotifyServerSubscribersOfDataChange( CloudData *cloudData, CloudKey &key, bool wasUpdated );
|
||||
|
||||
struct RemoteServer
|
||||
{
|
||||
RakNetGUID serverAddress;
|
||||
// This server needs to know about these keys when they are updated or deleted
|
||||
DataStructures::OrderedList<CloudKey,CloudKey,CloudKeyComp> subscribedKeys;
|
||||
// This server has uploaded these keys, and needs to know about Get() requests
|
||||
DataStructures::OrderedList<CloudKey,CloudKey,CloudKeyComp> uploadedKeys;
|
||||
|
||||
// Just for processing
|
||||
bool workingFlag;
|
||||
|
||||
// If false, we don't know what keys they have yet, so send everything
|
||||
bool gotSubscribedAndUploadedKeys;
|
||||
};
|
||||
|
||||
static int RemoteServerComp(const RakNetGUID &key, RemoteServer* const &data );
|
||||
DataStructures::OrderedList<RakNetGUID, RemoteServer*, CloudServer::RemoteServerComp> remoteServers;
|
||||
|
||||
struct BufferedGetResponseFromServer
|
||||
{
|
||||
void Clear(CloudAllocator *allocator);
|
||||
|
||||
RakNetGUID serverAddress;
|
||||
CloudQueryResult queryResult;
|
||||
bool gotResult;
|
||||
};
|
||||
|
||||
struct CloudQueryWithAddresses
|
||||
{
|
||||
// Inputs
|
||||
CloudQuery cloudQuery;
|
||||
DataStructures::List<RakNetGUID> specificSystems;
|
||||
|
||||
void Serialize(bool writeToBitstream, BitStream *bitStream);
|
||||
};
|
||||
|
||||
static int BufferedGetResponseFromServerComp(const RakNetGUID &key, BufferedGetResponseFromServer* const &data );
|
||||
struct GetRequest
|
||||
{
|
||||
void Clear(CloudAllocator *allocator);
|
||||
bool AllRemoteServersHaveResponded(void) const;
|
||||
CloudQueryWithAddresses cloudQueryWithAddresses;
|
||||
|
||||
// When request started. If takes too long for a response from another system, can abort remaining systems
|
||||
RakNet::Time requestStartTime;
|
||||
|
||||
// Assigned by server that gets the request to identify response. See nextGetRequestId
|
||||
uint32_t requestId;
|
||||
|
||||
RakNetGUID requestingClient;
|
||||
|
||||
DataStructures::OrderedList<RakNetGUID, BufferedGetResponseFromServer*, CloudServer::BufferedGetResponseFromServerComp> remoteServerResponses;
|
||||
};
|
||||
static int GetRequestComp(const uint32_t &key, GetRequest* const &data );
|
||||
DataStructures::OrderedList<uint32_t, GetRequest*, CloudServer::GetRequestComp> getRequests;
|
||||
RakNet::Time nextGetRequestsCheck;
|
||||
|
||||
uint32_t nextGetRequestId;
|
||||
|
||||
void ProcessAndTransmitGetRequest(GetRequest *getRequest);
|
||||
|
||||
void ProcessCloudQueryWithAddresses(
|
||||
CloudServer::CloudQueryWithAddresses &cloudQueryWithAddresses,
|
||||
DataStructures::List<CloudData*> &cloudDataResultList,
|
||||
DataStructures::List<CloudKey> &cloudKeyResultList
|
||||
);
|
||||
|
||||
void SendUploadedAndSubscribedKeysToServer( RakNetGUID systemAddress );
|
||||
void SendUploadedKeyToServers( CloudKey &cloudKey );
|
||||
void SendSubscribedKeyToServers( CloudKey &cloudKey );
|
||||
void RemoveUploadedKeyFromServers( CloudKey &cloudKey );
|
||||
void RemoveSubscribedKeyFromServers( CloudKey &cloudKey );
|
||||
|
||||
void OnSendUploadedAndSubscribedKeysToServer( Packet *packet );
|
||||
void OnSendUploadedKeyToServers( Packet *packet );
|
||||
void OnSendSubscribedKeyToServers( Packet *packet );
|
||||
void OnRemoveUploadedKeyFromServers( Packet *packet );
|
||||
void OnRemoveSubscribedKeyFromServers( Packet *packet );
|
||||
void OnServerDataChanged( Packet *packet );
|
||||
|
||||
void GetServersWithUploadedKeys(
|
||||
DataStructures::List<CloudKey> &keys,
|
||||
DataStructures::List<RemoteServer*> &remoteServersWithData
|
||||
);
|
||||
|
||||
CloudServer::CloudDataList *GetOrAllocateCloudDataList(CloudKey key, bool *dataRepositoryExists, unsigned int &dataRepositoryIndex);
|
||||
|
||||
void UnsubscribeFromKey(RemoteCloudClient *remoteCloudClient, RakNetGUID remoteCloudClientGuid, unsigned int keySubscriberIndex, CloudKey &cloudKey, DataStructures::List<RakNetGUID> &specificSystems);
|
||||
void RemoveSpecificSubscriber(RakNetGUID specificSubscriber, CloudDataList *cloudDataList, RakNetGUID remoteCloudClientGuid);
|
||||
|
||||
DataStructures::List<CloudServerQueryFilter*> queryFilters;
|
||||
|
||||
SystemAddress forceAddress;
|
||||
};
|
||||
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
// Key subscription
|
||||
//
|
||||
// A given system can subscribe to one or more keys.
|
||||
// The subscription can be further be defined as only subscribing to keys uploaded by or changed by a given system.
|
||||
// It is possible to subscribe to keys not yet uploaded, or uploaded to another system
|
||||
//
|
||||
// Operations:
|
||||
//
|
||||
// 1. SubscribeToKey() - Get() operation with subscription
|
||||
// A. Add to key subscription list for the client, which contains a keyId / specificUploaderList pair
|
||||
// B. Send to remote servers that for this key, they should send us updates
|
||||
// C. (Done, get operation returns current values)
|
||||
//
|
||||
// 2. UpdateData() - Post() operation
|
||||
// A. Find all subscribers to this data, for the uploading system.
|
||||
// B. Send them the uploaded data
|
||||
// C. Find all servers that subscribe to this data
|
||||
// D. Send them the uploaded data
|
||||
//
|
||||
// 3. DeleteData() - Release() operation
|
||||
// A. Find all subscribers to this data, for the deleting system.
|
||||
// B. Inform them of the deletion
|
||||
// C. Find all servers that subscribe to this data
|
||||
// D. Inform them of the deletion
|
||||
//
|
||||
// 4. Unsubscribe()
|
||||
// A. Find this subscriber, and remove their subscription
|
||||
// B. If no one else is subscribing to this key for any system, notify remote servers we no longer need subscription updates
|
||||
//
|
||||
// Internal operations:
|
||||
//
|
||||
// 1. Find if any connected client has subscribed to a given key
|
||||
// A. This is used add and remove our subscription for this key to remote servers
|
||||
//
|
||||
// 2. For a given key and updating address, find all connected clients that care
|
||||
// A. First find connected clients that have subscribed to this key, regardless of address
|
||||
// B. Then find connected clients that have subscribed to this key for this particular address
|
||||
//
|
||||
// 3. Find all remote servers that have subscribed to a given key
|
||||
// A. This is so when the key is updated or deleted, we know who to send it to
|
||||
//
|
||||
// 4. For a given client (such as on disconnect), remove all records of their subscriptions
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
161
third-party/RakNet/src/RakNet/CommandParserInterface.cpp
vendored
Normal file
161
third-party/RakNet/src/RakNet/CommandParserInterface.cpp
vendored
Normal file
@@ -0,0 +1,161 @@
|
||||
#include "CommandParserInterface.hpp"
|
||||
#include "TransportInterface.hpp"
|
||||
#include <string.h>
|
||||
#include "RakAssert.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
// IP_DONTFRAGMENT is different between winsock 1 and winsock 2. Therefore, Winsock2.h must be linked againt Ws2_32.lib
|
||||
// winsock.h must be linked against WSock32.lib. If these two are mixed up the flag won't work correctly
|
||||
#include <winsock2.h>
|
||||
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
const unsigned char CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS=255;
|
||||
|
||||
int RakNet::RegisteredCommandComp( const char* const & key, const RegisteredCommand &data )
|
||||
{
|
||||
return _stricmp(key,data.command);
|
||||
}
|
||||
|
||||
CommandParserInterface::CommandParserInterface() {}
|
||||
CommandParserInterface::~CommandParserInterface() {}
|
||||
|
||||
void CommandParserInterface::ParseConsoleString(char *str, const char delineator, unsigned char delineatorToggle, unsigned *numParameters, char **parameterList, unsigned parameterListLength)
|
||||
{
|
||||
unsigned strIndex, parameterListIndex;
|
||||
unsigned strLen;
|
||||
bool replaceDelineator=true;
|
||||
|
||||
strLen = (unsigned) strlen(str);
|
||||
|
||||
// Replace every instance of delineator, \n, \r with 0
|
||||
for (strIndex=0; strIndex < strLen; strIndex++)
|
||||
{
|
||||
if (str[strIndex]==delineator && replaceDelineator)
|
||||
str[strIndex]=0;
|
||||
|
||||
if (str[strIndex]=='\n' || str[strIndex]=='\r')
|
||||
str[strIndex]=0;
|
||||
|
||||
if (str[strIndex]==delineatorToggle)
|
||||
{
|
||||
str[strIndex]=0;
|
||||
replaceDelineator=!replaceDelineator;
|
||||
}
|
||||
}
|
||||
|
||||
// Fill up parameterList starting at each non-0
|
||||
for (strIndex=0, parameterListIndex=0; strIndex < strLen; )
|
||||
{
|
||||
if (str[strIndex]!=0)
|
||||
{
|
||||
parameterList[parameterListIndex]=str+strIndex;
|
||||
parameterListIndex++;
|
||||
RakAssert(parameterListIndex < parameterListLength);
|
||||
if (parameterListIndex >= parameterListLength)
|
||||
break;
|
||||
|
||||
strIndex++;
|
||||
while (str[strIndex]!=0 && strIndex < strLen)
|
||||
strIndex++;
|
||||
}
|
||||
else
|
||||
strIndex++;
|
||||
}
|
||||
|
||||
parameterList[parameterListIndex]=0;
|
||||
*numParameters=parameterListIndex;
|
||||
}
|
||||
void CommandParserInterface::SendCommandList(TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
unsigned i;
|
||||
if (commandList.Size())
|
||||
{
|
||||
for (i=0; i < commandList.Size(); i++)
|
||||
{
|
||||
transport->Send(systemAddress, "%s", commandList[i].command);
|
||||
if (i < commandList.Size()-1)
|
||||
transport->Send(systemAddress, ", ");
|
||||
}
|
||||
transport->Send(systemAddress, "\r\n");
|
||||
}
|
||||
else
|
||||
transport->Send(systemAddress, "No registered commands\r\n");
|
||||
}
|
||||
void CommandParserInterface::RegisterCommand(unsigned char parameterCount, const char *command, const char *commandHelp)
|
||||
{
|
||||
RegisteredCommand rc;
|
||||
rc.command=command;
|
||||
rc.commandHelp=commandHelp;
|
||||
rc.parameterCount=parameterCount;
|
||||
commandList.Insert( command, rc, true, _FILE_AND_LINE_);
|
||||
}
|
||||
bool CommandParserInterface::GetRegisteredCommand(const char *command, RegisteredCommand *rc)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index=commandList.GetIndexFromKey(command, &objectExists);
|
||||
if (objectExists)
|
||||
*rc=commandList[index];
|
||||
return objectExists;
|
||||
}
|
||||
void CommandParserInterface::OnTransportChange(TransportInterface *transport)
|
||||
{
|
||||
(void) transport;
|
||||
}
|
||||
void CommandParserInterface::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport)
|
||||
{
|
||||
(void) systemAddress;
|
||||
(void) transport;
|
||||
}
|
||||
void CommandParserInterface::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport)
|
||||
{
|
||||
(void) systemAddress;
|
||||
(void) transport;
|
||||
}
|
||||
void CommandParserInterface::ReturnResult(bool res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
if (res)
|
||||
transport->Send(systemAddress, "%s returned true.\r\n", command);
|
||||
else
|
||||
transport->Send(systemAddress, "%s returned false.\r\n", command);
|
||||
}
|
||||
void CommandParserInterface::ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress, "%s returned %i.\r\n", command, res);
|
||||
}
|
||||
void CommandParserInterface::ReturnResult(const char *command, TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress, "Successfully called %s.\r\n", command);
|
||||
}
|
||||
void CommandParserInterface::ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress, "%s returned %s.\r\n", command, res);
|
||||
}
|
||||
void CommandParserInterface::ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
char addr[128];
|
||||
systemAddress.ToString(false,addr);
|
||||
char addr2[128];
|
||||
res.ToString(false,addr2);
|
||||
transport->Send(systemAddress, "%s returned %s %s:%i\r\n", command,addr,addr2,res.GetPort());
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
140
third-party/RakNet/src/RakNet/CommandParserInterface.hpp
vendored
Normal file
140
third-party/RakNet/src/RakNet/CommandParserInterface.hpp
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
/// \file CommandParserInterface.h
|
||||
/// \brief Contains CommandParserInterface , from which you derive custom command parsers
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __COMMAND_PARSER_INTERFACE
|
||||
#define __COMMAND_PARSER_INTERFACE
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "DS_OrderedList.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
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 );
|
||||
|
||||
/// \brief The interface used by command parsers.
|
||||
/// \details 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.
|
||||
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;
|
||||
|
||||
/// \brief 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(const SystemAddress &systemAddress, TransportInterface *transport);
|
||||
|
||||
/// \brief 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(const SystemAddress &systemAddress, TransportInterface *transport);
|
||||
|
||||
/// \brief 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, const SystemAddress &systemAddress)=0;
|
||||
|
||||
/// \brief 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, const SystemAddress &systemAddress, const char *originalString)=0;
|
||||
|
||||
/// \brief This is called every time transport interface is registered.
|
||||
/// \details 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, const SystemAddress &systemAddress);
|
||||
|
||||
static const unsigned char VARIABLE_NUMBER_OF_PARAMETERS;
|
||||
|
||||
// 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);
|
||||
|
||||
/// \brief Just writes a string to the remote system based on the result ( \a res ) of your operation
|
||||
/// \details 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, const SystemAddress &systemAddress);
|
||||
virtual void ReturnResult(char *res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress);
|
||||
virtual void ReturnResult(SystemAddress res, const char *command, TransportInterface *transport, const SystemAddress &systemAddress);
|
||||
virtual void ReturnResult(int res, const char *command,TransportInterface *transport, const SystemAddress &systemAddress);
|
||||
|
||||
/// \brief Just writes a string to the remote system when you are calling a function that has no return value.
|
||||
/// \details 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, const SystemAddress &systemAddress);
|
||||
|
||||
protected:
|
||||
DataStructures::OrderedList<const char*, RegisteredCommand, RegisteredCommandComp> commandList;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
299
third-party/RakNet/src/RakNet/ConnectionGraph2.cpp
vendored
Normal file
299
third-party/RakNet/src/RakNet/ConnectionGraph2.cpp
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_ConnectionGraph2==1
|
||||
|
||||
#include "ConnectionGraph2.hpp"
|
||||
#include "RakPeerInterface.hpp"
|
||||
#include "MessageIdentifiers.hpp"
|
||||
#include "BitStream.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(ConnectionGraph2,ConnectionGraph2);
|
||||
|
||||
int RakNet::ConnectionGraph2::RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data )
|
||||
{
|
||||
if (key < data->guid)
|
||||
return -1;
|
||||
if (key > data->guid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RakNet::ConnectionGraph2::SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data )
|
||||
{
|
||||
if (key.guid<data.guid)
|
||||
return -1;
|
||||
if (key.guid>data.guid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
ConnectionGraph2::ConnectionGraph2()
|
||||
{
|
||||
autoProcessNewConnections=true;
|
||||
}
|
||||
ConnectionGraph2::~ConnectionGraph2()
|
||||
{
|
||||
|
||||
}
|
||||
bool ConnectionGraph2::GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength)
|
||||
{
|
||||
if ((saOut==0 && guidOut==0) || outLength==0 || *outLength==0 || remoteSystemGuid==UNASSIGNED_RAKNET_GUID)
|
||||
{
|
||||
*outLength=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool objectExists;
|
||||
unsigned int idx = remoteSystems.GetIndexFromKey(remoteSystemGuid, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
*outLength=0;
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int idx2;
|
||||
if (remoteSystems[idx]->remoteConnections.Size() < *outLength)
|
||||
*outLength=remoteSystems[idx]->remoteConnections.Size();
|
||||
for (idx2=0; idx2 < *outLength; idx2++)
|
||||
{
|
||||
if (guidOut)
|
||||
guidOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].guid;
|
||||
if (saOut)
|
||||
saOut[idx2]=remoteSystems[idx]->remoteConnections[idx2].systemAddress;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool ConnectionGraph2::ConnectionExists(RakNetGUID g1, RakNetGUID g2)
|
||||
{
|
||||
if (g1==g2)
|
||||
return false;
|
||||
|
||||
bool objectExists;
|
||||
unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SystemAddressAndGuid sag;
|
||||
sag.guid=g2;
|
||||
return remoteSystems[idx]->remoteConnections.HasData(sag);
|
||||
}
|
||||
uint16_t ConnectionGraph2::GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const
|
||||
{
|
||||
if (g1==g2)
|
||||
return 0;
|
||||
|
||||
if (g1==rakPeerInterface->GetMyGUID())
|
||||
return (uint16_t) rakPeerInterface->GetAveragePing(g2);
|
||||
if (g2==rakPeerInterface->GetMyGUID())
|
||||
return (uint16_t) rakPeerInterface->GetAveragePing(g1);
|
||||
|
||||
bool objectExists;
|
||||
unsigned int idx = remoteSystems.GetIndexFromKey(g1, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
return (uint16_t) -1;
|
||||
}
|
||||
|
||||
SystemAddressAndGuid sag;
|
||||
sag.guid=g2;
|
||||
unsigned int idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(sag, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
return (uint16_t) -1;
|
||||
}
|
||||
return remoteSystems[idx]->remoteConnections[idx2].sendersPingToThatSystem;
|
||||
}
|
||||
|
||||
/// Returns the system with the lowest total ping among all its connections. This can be used as the 'best host' for a peer to peer session
|
||||
RakNetGUID ConnectionGraph2::GetLowestAveragePingSystem(void) const
|
||||
{
|
||||
float lowestPing=-1.0;
|
||||
unsigned int lowestPingIdx=(unsigned int) -1;
|
||||
float thisAvePing=0.0f;
|
||||
unsigned int idx, idx2;
|
||||
int ap, count=0;
|
||||
|
||||
for (idx=0; idx<remoteSystems.Size(); idx++)
|
||||
{
|
||||
thisAvePing=0.0f;
|
||||
|
||||
ap = rakPeerInterface->GetAveragePing(remoteSystems[idx]->guid);
|
||||
if (ap!=-1)
|
||||
{
|
||||
thisAvePing+=(float) ap;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count>0)
|
||||
{
|
||||
lowestPing=thisAvePing/count;
|
||||
}
|
||||
|
||||
for (idx=0; idx<remoteSystems.Size(); idx++)
|
||||
{
|
||||
thisAvePing=0.0f;
|
||||
count=0;
|
||||
|
||||
RemoteSystem *remoteSystem = remoteSystems[idx];
|
||||
for (idx2=0; idx2 < remoteSystem->remoteConnections.Size(); idx2++)
|
||||
{
|
||||
ap=remoteSystem->remoteConnections[idx2].sendersPingToThatSystem;
|
||||
if (ap!=-1)
|
||||
{
|
||||
thisAvePing+=(float) ap;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (count>0 && (lowestPing==-1.0f || thisAvePing/count < lowestPing))
|
||||
{
|
||||
lowestPing=thisAvePing/count;
|
||||
lowestPingIdx=idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (lowestPingIdx==(unsigned int) -1)
|
||||
return rakPeerInterface->GetMyGUID();
|
||||
return remoteSystems[lowestPingIdx]->guid;
|
||||
}
|
||||
|
||||
void ConnectionGraph2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
||||
{
|
||||
// Send notice to all existing connections
|
||||
RakNet::BitStream bs;
|
||||
if (lostConnectionReason==LCR_CONNECTION_LOST)
|
||||
bs.Write((MessageID)ID_REMOTE_CONNECTION_LOST);
|
||||
else
|
||||
bs.Write((MessageID)ID_REMOTE_DISCONNECTION_NOTIFICATION);
|
||||
bs.Write(systemAddress);
|
||||
bs.Write(rakNetGUID);
|
||||
SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true);
|
||||
|
||||
bool objectExists;
|
||||
unsigned int idx = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
RakNet::OP_DELETE(remoteSystems[idx],_FILE_AND_LINE_);
|
||||
remoteSystems.RemoveAtIndex(idx);
|
||||
}
|
||||
}
|
||||
void ConnectionGraph2::SetAutoProcessNewConnections(bool b)
|
||||
{
|
||||
autoProcessNewConnections=b;
|
||||
}
|
||||
bool ConnectionGraph2::GetAutoProcessNewConnections(void) const
|
||||
{
|
||||
return autoProcessNewConnections;
|
||||
}
|
||||
void ConnectionGraph2::AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID)
|
||||
{
|
||||
// Relay the new connection to other systems.
|
||||
RakNet::BitStream bs;
|
||||
bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION);
|
||||
bs.Write((uint32_t)1);
|
||||
bs.Write(systemAddress);
|
||||
bs.Write(rakNetGUID);
|
||||
bs.WriteCasted<uint16_t>(rakPeerInterface->GetAveragePing(rakNetGUID));
|
||||
SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,true);
|
||||
|
||||
// Send everyone to the new guy
|
||||
DataStructures::List<SystemAddress> addresses;
|
||||
DataStructures::List<RakNetGUID> guids;
|
||||
rakPeerInterface->GetSystemList(addresses, guids);
|
||||
bs.Reset();
|
||||
bs.Write((MessageID)ID_REMOTE_NEW_INCOMING_CONNECTION);
|
||||
BitSize_t writeOffset = bs.GetWriteOffset();
|
||||
bs.Write((uint32_t) addresses.Size());
|
||||
|
||||
unsigned int i;
|
||||
uint32_t count=0;
|
||||
for (i=0; i < addresses.Size(); i++)
|
||||
{
|
||||
if (addresses[i]==systemAddress)
|
||||
continue;
|
||||
|
||||
bs.Write(addresses[i]);
|
||||
bs.Write(guids[i]);
|
||||
bs.WriteCasted<uint16_t>(rakPeerInterface->GetAveragePing(guids[i]));
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count>0)
|
||||
{
|
||||
BitSize_t writeOffset2 = bs.GetWriteOffset();
|
||||
bs.SetWriteOffset(writeOffset);
|
||||
bs.Write(count);
|
||||
bs.SetWriteOffset(writeOffset2);
|
||||
SendUnified(&bs,HIGH_PRIORITY,RELIABLE_ORDERED,0,systemAddress,false);
|
||||
}
|
||||
|
||||
bool objectExists;
|
||||
unsigned int ii = remoteSystems.GetIndexFromKey(rakNetGUID, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
RemoteSystem* remoteSystem = RakNet::OP_NEW<RemoteSystem>(_FILE_AND_LINE_);
|
||||
remoteSystem->guid=rakNetGUID;
|
||||
remoteSystems.InsertAtIndex(remoteSystem,ii,_FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
void ConnectionGraph2::GetParticipantList(DataStructures::OrderedList<RakNetGUID, RakNetGUID> &participantList)
|
||||
{
|
||||
participantList.Clear(true, _FILE_AND_LINE_);
|
||||
unsigned int i;
|
||||
for (i=0; i < remoteSystems.Size(); i++)
|
||||
participantList.InsertAtEnd(remoteSystems[i]->guid, _FILE_AND_LINE_);
|
||||
}
|
||||
void ConnectionGraph2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
||||
{
|
||||
(void) isIncoming;
|
||||
if (autoProcessNewConnections)
|
||||
AddParticipant(systemAddress, rakNetGUID);
|
||||
}
|
||||
PluginReceiveResult ConnectionGraph2::OnReceive(Packet *packet)
|
||||
{
|
||||
if (packet->data[0]==ID_REMOTE_CONNECTION_LOST || packet->data[0]==ID_REMOTE_DISCONNECTION_NOTIFICATION)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
RakNet::BitStream bs(packet->data,packet->length,false);
|
||||
bs.IgnoreBytes(1);
|
||||
SystemAddressAndGuid saag;
|
||||
bs.Read(saag.systemAddress);
|
||||
bs.Read(saag.guid);
|
||||
unsigned long idx2 = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists);
|
||||
if (objectExists)
|
||||
remoteSystems[idx]->remoteConnections.RemoveAtIndex(idx2);
|
||||
}
|
||||
}
|
||||
else if (packet->data[0]==ID_REMOTE_NEW_INCOMING_CONNECTION)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned idx = remoteSystems.GetIndexFromKey(packet->guid, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
uint32_t numAddresses;
|
||||
RakNet::BitStream bs(packet->data,packet->length,false);
|
||||
bs.IgnoreBytes(1);
|
||||
bs.Read(numAddresses);
|
||||
for (unsigned int idx2=0; idx2 < numAddresses; idx2++)
|
||||
{
|
||||
SystemAddressAndGuid saag;
|
||||
bs.Read(saag.systemAddress);
|
||||
bs.Read(saag.guid);
|
||||
bs.Read(saag.sendersPingToThatSystem);
|
||||
bool objectExists;
|
||||
unsigned int ii = remoteSystems[idx]->remoteConnections.GetIndexFromKey(saag, &objectExists);
|
||||
if (objectExists==false)
|
||||
remoteSystems[idx]->remoteConnections.InsertAtIndex(saag,ii,_FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
118
third-party/RakNet/src/RakNet/ConnectionGraph2.hpp
vendored
Normal file
118
third-party/RakNet/src/RakNet/ConnectionGraph2.hpp
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
/// \file ConnectionGraph2.h
|
||||
/// \brief Connection graph plugin, version 2. Tells new systems about existing and new connections
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_ConnectionGraph2==1
|
||||
|
||||
#ifndef __CONNECTION_GRAPH_2_H
|
||||
#define __CONNECTION_GRAPH_2_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "DS_WeightedGraph.hpp"
|
||||
#include "GetTime.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
|
||||
/// \brief A one hop connection graph.
|
||||
/// \details Sends ID_REMOTE_CONNECTION_LOST, ID_REMOTE_DISCONNECTION_NOTIFICATION, ID_REMOTE_NEW_INCOMING_CONNECTION<BR>
|
||||
/// All identifiers are followed by SystemAddress, then RakNetGUID
|
||||
/// Also stores the list for you, which you can access with GetConnectionListForRemoteSystem
|
||||
/// \ingroup CONNECTION_GRAPH_GROUP
|
||||
class RAK_DLL_EXPORT ConnectionGraph2 : public PluginInterface2
|
||||
{
|
||||
public:
|
||||
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(ConnectionGraph2)
|
||||
|
||||
ConnectionGraph2();
|
||||
~ConnectionGraph2();
|
||||
|
||||
/// \brief Given a remote system identified by RakNetGUID, return the list of SystemAddresses and RakNetGUIDs they are connected to
|
||||
/// \param[in] remoteSystemGuid Which system we are referring to. This only works for remote systems, not ourselves.
|
||||
/// \param[out] saOut A preallocated array to hold the output list of SystemAddress. Can be 0 if you don't care.
|
||||
/// \param[out] guidOut A preallocated array to hold the output list of RakNetGUID. Can be 0 if you don't care.
|
||||
/// \param[in,out] outLength On input, the size of \a saOut and \a guidOut. On output, modified to reflect the number of elements actually written
|
||||
/// \return True if \a remoteSystemGuid was found. Otherwise false, and \a saOut, \a guidOut remain unchanged. \a outLength will be set to 0.
|
||||
bool GetConnectionListForRemoteSystem(RakNetGUID remoteSystemGuid, SystemAddress *saOut, RakNetGUID *guidOut, unsigned int *outLength);
|
||||
|
||||
/// Returns if g1 is connected to g2
|
||||
bool ConnectionExists(RakNetGUID g1, RakNetGUID g2);
|
||||
|
||||
/// Returns the average ping between two systems in the connection graph. Returns -1 if no connection exists between those systems
|
||||
uint16_t GetPingBetweenSystems(RakNetGUID g1, RakNetGUID g2) const;
|
||||
|
||||
/// Returns the system with the lowest average ping among all its connections.
|
||||
/// If you need one system in the peer to peer group to relay data, have the FullyConnectedMesh2 host call this function after host migration, and use that system
|
||||
RakNetGUID GetLowestAveragePingSystem(void) const;
|
||||
|
||||
/// \brief If called with false, then new connections are only added to the connection graph when you call ProcessNewConnection();
|
||||
/// \details This is useful if you want to perform validation before connecting a system to a mesh, or if you want a submesh (for example a server cloud)
|
||||
/// \param[in] b True to automatically call ProcessNewConnection() on any new connection, false to not do so. Defaults to true.
|
||||
void SetAutoProcessNewConnections(bool b);
|
||||
|
||||
/// \brief Returns value passed to SetAutoProcessNewConnections()
|
||||
/// \return Value passed to SetAutoProcessNewConnections(), or the default of true if it was never called
|
||||
bool GetAutoProcessNewConnections(void) const;
|
||||
|
||||
/// \brief If you call SetAutoProcessNewConnections(false);, then you will need to manually call ProcessNewConnection() on new connections
|
||||
/// \details On ID_NEW_INCOMING_CONNECTION or ID_CONNECTION_REQUEST_ACCEPTED, adds that system to the graph
|
||||
/// Do not call ProcessNewConnection() manually otherwise
|
||||
/// \param[in] The packet->SystemAddress member
|
||||
/// \param[in] The packet->guid member
|
||||
void AddParticipant(const SystemAddress &systemAddress, RakNetGUID rakNetGUID);
|
||||
|
||||
/// Get the participants added with AddParticipant()
|
||||
/// \param[out] participantList Participants added with AddParticipant();
|
||||
void GetParticipantList(DataStructures::OrderedList<RakNetGUID, RakNetGUID> &participantList);
|
||||
|
||||
/// \internal
|
||||
struct SystemAddressAndGuid
|
||||
{
|
||||
SystemAddress systemAddress;
|
||||
RakNetGUID guid;
|
||||
uint16_t sendersPingToThatSystem;
|
||||
};
|
||||
/// \internal
|
||||
static int SystemAddressAndGuidComp( const SystemAddressAndGuid &key, const SystemAddressAndGuid &data );
|
||||
|
||||
/// \internal
|
||||
struct RemoteSystem
|
||||
{
|
||||
DataStructures::OrderedList<SystemAddressAndGuid,SystemAddressAndGuid,ConnectionGraph2::SystemAddressAndGuidComp> remoteConnections;
|
||||
RakNetGUID guid;
|
||||
};
|
||||
/// \internal
|
||||
static int RemoteSystemComp( const RakNetGUID &key, RemoteSystem * const &data );
|
||||
|
||||
protected:
|
||||
/// \internal
|
||||
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
|
||||
/// \internal
|
||||
virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
|
||||
/// \internal
|
||||
virtual PluginReceiveResult OnReceive(Packet *packet);
|
||||
|
||||
// List of systems I am connected to, which in turn stores which systems they are connected to
|
||||
DataStructures::OrderedList<RakNetGUID, RemoteSystem*, ConnectionGraph2::RemoteSystemComp> remoteSystems;
|
||||
|
||||
bool autoProcessNewConnections;
|
||||
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif // #ifndef __CONNECTION_GRAPH_2_H
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
311
third-party/RakNet/src/RakNet/ConsoleServer.cpp
vendored
Normal file
311
third-party/RakNet/src/RakNet/ConsoleServer.cpp
vendored
Normal file
@@ -0,0 +1,311 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_ConsoleServer==1
|
||||
|
||||
#include "ConsoleServer.hpp"
|
||||
#include "TransportInterface.hpp"
|
||||
#include "CommandParserInterface.hpp"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define COMMAND_DELINATOR ' '
|
||||
#define COMMAND_DELINATOR_TOGGLE '"'
|
||||
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(ConsoleServer,ConsoleServer);
|
||||
|
||||
ConsoleServer::ConsoleServer()
|
||||
{
|
||||
transport=0;
|
||||
password[0]=0;
|
||||
prompt=0;
|
||||
}
|
||||
ConsoleServer::~ConsoleServer()
|
||||
{
|
||||
if (prompt)
|
||||
rakFree_Ex(prompt, _FILE_AND_LINE_);
|
||||
}
|
||||
void ConsoleServer::SetTransportProvider(TransportInterface *transportInterface, unsigned short port)
|
||||
{
|
||||
// Replace the current TransportInterface, stopping the old one, if present, and starting the new one.
|
||||
if (transportInterface)
|
||||
{
|
||||
if (transport)
|
||||
{
|
||||
RemoveCommandParser(transport->GetCommandParser());
|
||||
transport->Stop();
|
||||
}
|
||||
transport=transportInterface;
|
||||
transport->Start(port, true);
|
||||
|
||||
unsigned i;
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
commandParserList[i]->OnTransportChange(transport);
|
||||
|
||||
// The transport itself might have a command parser - for example password for the RakNet transport
|
||||
AddCommandParser(transport->GetCommandParser());
|
||||
}
|
||||
}
|
||||
void ConsoleServer::AddCommandParser(CommandParserInterface *commandParserInterface)
|
||||
{
|
||||
if (commandParserInterface==0)
|
||||
return;
|
||||
|
||||
// Non-duplicate insertion
|
||||
unsigned i;
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
if (commandParserList[i]==commandParserInterface)
|
||||
return;
|
||||
|
||||
if (_stricmp(commandParserList[i]->GetName(), commandParserInterface->GetName())==0)
|
||||
{
|
||||
// Naming conflict between two command parsers
|
||||
RakAssert(0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
commandParserList.Insert(commandParserInterface, _FILE_AND_LINE_);
|
||||
if (transport)
|
||||
commandParserInterface->OnTransportChange(transport);
|
||||
}
|
||||
void ConsoleServer::RemoveCommandParser(CommandParserInterface *commandParserInterface)
|
||||
{
|
||||
if (commandParserInterface==0)
|
||||
return;
|
||||
|
||||
// Overwrite the element we are removing from the back of the list and delete the back of the list
|
||||
unsigned i;
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
if (commandParserList[i]==commandParserInterface)
|
||||
{
|
||||
commandParserList[i]=commandParserList[commandParserList.Size()-1];
|
||||
commandParserList.RemoveFromEnd();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
void ConsoleServer::Update(void)
|
||||
{
|
||||
unsigned i;
|
||||
char *parameterList[20]; // Up to 20 parameters
|
||||
unsigned numParameters;
|
||||
RakNet::SystemAddress newOrLostConnectionId;
|
||||
RakNet::Packet *p;
|
||||
RakNet::RegisteredCommand rc;
|
||||
|
||||
p = transport->Receive();
|
||||
newOrLostConnectionId=transport->HasNewIncomingConnection();
|
||||
|
||||
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
commandParserList[i]->OnNewIncomingConnection(newOrLostConnectionId, transport);
|
||||
}
|
||||
|
||||
transport->Send(newOrLostConnectionId, "Connected to remote command console.\r\nType 'help' for help.\r\n");
|
||||
ListParsers(newOrLostConnectionId);
|
||||
ShowPrompt(newOrLostConnectionId);
|
||||
}
|
||||
|
||||
newOrLostConnectionId=transport->HasLostConnection();
|
||||
if (newOrLostConnectionId!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
commandParserList[i]->OnConnectionLost(newOrLostConnectionId, transport);
|
||||
}
|
||||
|
||||
while (p)
|
||||
{
|
||||
bool commandParsed=false;
|
||||
char copy[REMOTE_MAX_TEXT_INPUT];
|
||||
memcpy(copy, p->data, p->length);
|
||||
copy[p->length]=0;
|
||||
RakNet::CommandParserInterface::ParseConsoleString((char*)p->data, COMMAND_DELINATOR, COMMAND_DELINATOR_TOGGLE, &numParameters, parameterList, 20); // Up to 20 parameters
|
||||
if (numParameters==0)
|
||||
{
|
||||
transport->DeallocatePacket(p);
|
||||
p = transport->Receive();
|
||||
continue;
|
||||
}
|
||||
if (_stricmp(*parameterList, "help")==0 && numParameters<=2)
|
||||
{
|
||||
// Find the parser specified and display help for it
|
||||
if (numParameters==1)
|
||||
{
|
||||
transport->Send(p->systemAddress, "\r\nINSTRUCTIONS:\r\n");
|
||||
transport->Send(p->systemAddress, "Enter commands on your keyboard, using spaces to delineate parameters.\r\n");
|
||||
transport->Send(p->systemAddress, "You can use quotation marks to toggle space delineation.\r\n");
|
||||
transport->Send(p->systemAddress, "You can connect multiple times from the same computer.\r\n");
|
||||
transport->Send(p->systemAddress, "You can direct commands to a parser by prefixing the parser name or number.\r\n");
|
||||
transport->Send(p->systemAddress, "COMMANDS:\r\n");
|
||||
transport->Send(p->systemAddress, "help Show this display.\r\n");
|
||||
transport->Send(p->systemAddress, "help <ParserName> Show help on a particular parser.\r\n");
|
||||
transport->Send(p->systemAddress, "help <CommandName> Show help on a particular command.\r\n");
|
||||
transport->Send(p->systemAddress, "quit Disconnects from the server.\r\n");
|
||||
transport->Send(p->systemAddress, "[<ParserName>] <Command> [<Parameters>] Execute a command\r\n");
|
||||
transport->Send(p->systemAddress, "[<ParserNumber>] <Command> [<Parameters>] Execute a command\r\n");
|
||||
ListParsers(p->systemAddress);
|
||||
//ShowPrompt(p->systemAddress);
|
||||
}
|
||||
else // numParameters == 2, including the help tag
|
||||
{
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
if (_stricmp(parameterList[1], commandParserList[i]->GetName())==0)
|
||||
{
|
||||
commandParsed=true;
|
||||
commandParserList[i]->SendHelp(transport, p->systemAddress);
|
||||
transport->Send(p->systemAddress, "COMMAND LIST:\r\n");
|
||||
commandParserList[i]->SendCommandList(transport, p->systemAddress);
|
||||
transport->Send(p->systemAddress, "\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (commandParsed==false)
|
||||
{
|
||||
// Try again, for all commands for all parsers.
|
||||
RakNet::RegisteredCommand rc;
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
if (commandParserList[i]->GetRegisteredCommand(parameterList[1], &rc))
|
||||
{
|
||||
if (rc.parameterCount==RakNet::CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS)
|
||||
transport->Send(p->systemAddress, "(Variable parms): %s %s\r\n", rc.command, rc.commandHelp);
|
||||
else
|
||||
transport->Send(p->systemAddress, "(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
|
||||
commandParsed=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (commandParsed==false)
|
||||
{
|
||||
// Don't know what to do
|
||||
transport->Send(p->systemAddress, "Unknown help topic: %s.\r\n", parameterList[1]);
|
||||
}
|
||||
//ShowPrompt(p->systemAddress);
|
||||
}
|
||||
}
|
||||
else if (_stricmp(*parameterList, "quit")==0 && numParameters==1)
|
||||
{
|
||||
transport->Send(p->systemAddress, "Goodbye!\r\n");
|
||||
transport->CloseConnection(p->systemAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool tryAllParsers=true;
|
||||
bool failed=false;
|
||||
|
||||
if (numParameters >=2) // At minimum <CommandParserName> <Command>
|
||||
{
|
||||
unsigned commandParserIndex=(unsigned)-1;
|
||||
// Prefixing with numbers directs to a particular parser
|
||||
if (**parameterList>='0' && **parameterList<='9')
|
||||
{
|
||||
commandParserIndex=atoi(*parameterList); // Use specified parser unless it's an invalid number
|
||||
commandParserIndex--; // Subtract 1 since we displayed numbers starting at index+1
|
||||
if (commandParserIndex >= commandParserList.Size())
|
||||
{
|
||||
transport->Send(p->systemAddress, "Invalid index.\r\n");
|
||||
failed=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// // Prefixing with the name of a command parser directs to that parser. See if the first word matches a parser
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
if (_stricmp(parameterList[0], commandParserList[i]->GetName())==0)
|
||||
{
|
||||
commandParserIndex=i; // Matches parser at index i
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed==false)
|
||||
{
|
||||
// -1 means undirected, so otherwise this is directed to a target
|
||||
if (commandParserIndex!=(unsigned)-1)
|
||||
{
|
||||
// Only this parser should use this command
|
||||
tryAllParsers=false;
|
||||
if (commandParserList[commandParserIndex]->GetRegisteredCommand(parameterList[1], &rc))
|
||||
{
|
||||
commandParsed=true;
|
||||
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-2)
|
||||
commandParserList[commandParserIndex]->OnCommand(rc.command, numParameters-2, parameterList+2, transport, p->systemAddress, copy);
|
||||
else
|
||||
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failed == false && tryAllParsers)
|
||||
{
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
// Undirected command. Try all the parsers to see if they understand the command
|
||||
// Pass the 1nd element as the command, and the remainder as the parameter list
|
||||
if (commandParserList[i]->GetRegisteredCommand(parameterList[0], &rc))
|
||||
{
|
||||
commandParsed=true;
|
||||
|
||||
if (rc.parameterCount==CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS || rc.parameterCount==numParameters-1)
|
||||
commandParserList[i]->OnCommand(rc.command, numParameters-1, parameterList+1, transport, p->systemAddress, copy);
|
||||
else
|
||||
transport->Send(p->systemAddress, "Invalid parameter count.\r\n(%i parms): %s %s\r\n", rc.parameterCount, rc.command, rc.commandHelp);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (commandParsed==false && commandParserList.Size() > 0)
|
||||
{
|
||||
transport->Send(p->systemAddress, "Unknown command: Type 'help' for help.\r\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ShowPrompt(p->systemAddress);
|
||||
|
||||
transport->DeallocatePacket(p);
|
||||
p = transport->Receive();
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleServer::ListParsers(SystemAddress systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress,"INSTALLED PARSERS:\r\n");
|
||||
unsigned i;
|
||||
for (i=0; i < commandParserList.Size(); i++)
|
||||
{
|
||||
transport->Send(systemAddress, "%i. %s\r\n", i+1, commandParserList[i]->GetName());
|
||||
}
|
||||
}
|
||||
void ConsoleServer::ShowPrompt(SystemAddress systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress, prompt);
|
||||
}
|
||||
void ConsoleServer::SetPrompt(const char *_prompt)
|
||||
{
|
||||
if (prompt)
|
||||
rakFree_Ex(prompt,_FILE_AND_LINE_);
|
||||
if (_prompt && _prompt[0])
|
||||
{
|
||||
size_t len = strlen(_prompt);
|
||||
prompt = (char*) rakMalloc_Ex(len+1,_FILE_AND_LINE_);
|
||||
strcpy(prompt,_prompt);
|
||||
}
|
||||
else
|
||||
prompt=0;
|
||||
}
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
77
third-party/RakNet/src/RakNet/ConsoleServer.hpp
vendored
Normal file
77
third-party/RakNet/src/RakNet/ConsoleServer.hpp
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/// \file ConsoleServer.h
|
||||
/// \brief Contains ConsoleServer , used to plugin to your game to accept remote console-based connections
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_ConsoleServer==1
|
||||
|
||||
#ifndef __CONSOLE_SERVER_H
|
||||
#define __CONSOLE_SERVER_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class TransportInterface;
|
||||
class CommandParserInterface;
|
||||
|
||||
|
||||
/// \brief The main entry point for the server portion of your remote console application support.
|
||||
/// \details 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:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(ConsoleServer)
|
||||
|
||||
ConsoleServer();
|
||||
~ConsoleServer();
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief Add an implementation of CommandParserInterface to the list of command parsers.
|
||||
/// \param[in] commandParserInterface The command parser referred to
|
||||
void AddCommandParser(CommandParserInterface *commandParserInterface);
|
||||
|
||||
/// \brief Remove an implementation of CommandParserInterface previously added with AddCommandParser().
|
||||
/// \param[in] commandParserInterface The command parser referred to
|
||||
void RemoveCommandParser(CommandParserInterface *commandParserInterface);
|
||||
|
||||
/// \brief Call update to read packet sent from your TransportInterface.
|
||||
/// You should do this fairly frequently.
|
||||
void Update(void);
|
||||
|
||||
/// \brief Sets a prompt to show when waiting for user input.
|
||||
/// \details Pass an empty string to clear the prompt
|
||||
/// Defaults to no prompt
|
||||
/// \param[in] _prompt Null-terminated string of the prompt to use. If you want a newline, be sure to use /r/n
|
||||
void SetPrompt(const char *_prompt);
|
||||
|
||||
protected:
|
||||
void ListParsers(SystemAddress systemAddress);
|
||||
void ShowPrompt(SystemAddress systemAddress);
|
||||
TransportInterface *transport;
|
||||
DataStructures::List<CommandParserInterface *> commandParserList;
|
||||
char* password[256];
|
||||
char *prompt;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
1146
third-party/RakNet/src/RakNet/DS_BPlusTree.hpp
vendored
Normal file
1146
third-party/RakNet/src/RakNet/DS_BPlusTree.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1134
third-party/RakNet/src/RakNet/DS_BinarySearchTree.hpp
vendored
Normal file
1134
third-party/RakNet/src/RakNet/DS_BinarySearchTree.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
149
third-party/RakNet/src/RakNet/DS_BytePool.cpp
vendored
Normal file
149
third-party/RakNet/src/RakNet/DS_BytePool.cpp
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "DS_BytePool.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#ifndef __APPLE__
|
||||
// Use stdlib and not malloc for compatibility
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
using namespace DataStructures;
|
||||
|
||||
BytePool::BytePool()
|
||||
{
|
||||
pool128.SetPageSize(8192*4);
|
||||
pool512.SetPageSize(8192*4);
|
||||
pool2048.SetPageSize(8192*4);
|
||||
pool8192.SetPageSize(8192*4);
|
||||
}
|
||||
BytePool::~BytePool()
|
||||
{
|
||||
}
|
||||
void BytePool::SetPageSize(int size)
|
||||
{
|
||||
pool128.SetPageSize(size);
|
||||
pool512.SetPageSize(size);
|
||||
pool2048.SetPageSize(size);
|
||||
pool8192.SetPageSize(size);
|
||||
}
|
||||
unsigned char *BytePool::Allocate(int bytesWanted, const char *file, unsigned int line)
|
||||
{
|
||||
#ifdef _DISABLE_BYTE_POOL
|
||||
return rakMalloc_Ex(bytesWanted, _FILE_AND_LINE_);
|
||||
#endif
|
||||
unsigned char *out;
|
||||
if (bytesWanted <= 127)
|
||||
{
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex128.Lock();
|
||||
#endif
|
||||
out = (unsigned char*) pool128.Allocate(file, line);
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex128.Unlock();
|
||||
#endif
|
||||
out[0]=0;
|
||||
return ((unsigned char*) out)+1;
|
||||
}
|
||||
if (bytesWanted <= 511)
|
||||
{
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex512.Lock();
|
||||
#endif
|
||||
out = (unsigned char*) pool512.Allocate(file, line);
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex512.Unlock();
|
||||
#endif
|
||||
out[0]=1;
|
||||
return ((unsigned char*) out)+1;
|
||||
}
|
||||
if (bytesWanted <= 2047)
|
||||
{
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex2048.Lock();
|
||||
#endif
|
||||
out = (unsigned char*) pool2048.Allocate(file, line);
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex2048.Unlock();
|
||||
#endif
|
||||
out[0]=2;
|
||||
return ((unsigned char*) out)+1;
|
||||
}
|
||||
if (bytesWanted <= 8191)
|
||||
{
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex8192.Lock();
|
||||
#endif
|
||||
out = (unsigned char*) pool8192.Allocate(file, line);
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex8192.Unlock();
|
||||
#endif
|
||||
out[0]=3;
|
||||
return ((unsigned char*) out)+1;
|
||||
}
|
||||
|
||||
out = (unsigned char*) rakMalloc_Ex(bytesWanted+1, _FILE_AND_LINE_);
|
||||
out[0]=(unsigned char)255;
|
||||
return out+1;
|
||||
}
|
||||
void BytePool::Release(unsigned char *data, const char *file, unsigned int line)
|
||||
{
|
||||
#ifdef _DISABLE_BYTE_POOL
|
||||
_rakFree_Ex(data, _FILE_AND_LINE_ );
|
||||
#endif
|
||||
unsigned char *realData = data-1;
|
||||
switch (realData[0])
|
||||
{
|
||||
case 0:
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex128.Lock();
|
||||
#endif
|
||||
pool128.Release((unsigned char(*)[128]) realData, file, line );
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex128.Unlock();
|
||||
#endif
|
||||
break;
|
||||
case 1:
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex512.Lock();
|
||||
#endif
|
||||
pool512.Release((unsigned char(*)[512]) realData, file, line );
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex512.Unlock();
|
||||
#endif
|
||||
break;
|
||||
case 2:
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex2048.Lock();
|
||||
#endif
|
||||
pool2048.Release((unsigned char(*)[2048]) realData, file, line );
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex2048.Unlock();
|
||||
#endif
|
||||
break;
|
||||
case 3:
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex8192.Lock();
|
||||
#endif
|
||||
pool8192.Release((unsigned char(*)[8192]) realData, file, line );
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
mutex8192.Unlock();
|
||||
#endif
|
||||
break;
|
||||
case 255:
|
||||
rakFree_Ex(realData, file, line );
|
||||
break;
|
||||
default:
|
||||
RakAssert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void BytePool::Clear(const char *file, unsigned int line)
|
||||
{
|
||||
(void) file;
|
||||
(void) line;
|
||||
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
pool128.Clear(file, line);
|
||||
pool512.Clear(file, line);
|
||||
pool2048.Clear(file, line);
|
||||
pool8192.Clear(file, line);
|
||||
#endif
|
||||
}
|
||||
46
third-party/RakNet/src/RakNet/DS_BytePool.hpp
vendored
Normal file
46
third-party/RakNet/src/RakNet/DS_BytePool.hpp
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/// \file DS_BytePool.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __BYTE_POOL_H
|
||||
#define __BYTE_POOL_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_MemoryPool.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "SimpleMutex.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
|
||||
// #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, const char *file, unsigned int line);
|
||||
void Release(unsigned char *data, const char *file, unsigned int line);
|
||||
void Clear(const char *file, unsigned int line);
|
||||
protected:
|
||||
MemoryPool<unsigned char[128]> pool128;
|
||||
MemoryPool<unsigned char[512]> pool512;
|
||||
MemoryPool<unsigned char[2048]> pool2048;
|
||||
MemoryPool<unsigned char[8192]> pool8192;
|
||||
#ifdef _THREADSAFE_BYTE_POOL
|
||||
SimpleMutex mutex128;
|
||||
SimpleMutex mutex512;
|
||||
SimpleMutex mutex2048;
|
||||
SimpleMutex mutex8192;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
127
third-party/RakNet/src/RakNet/DS_ByteQueue.cpp
vendored
Normal file
127
third-party/RakNet/src/RakNet/DS_ByteQueue.cpp
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
#include "DS_ByteQueue.hpp"
|
||||
#include <string.h> // Memmove
|
||||
#include <stdlib.h> // realloc
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
using namespace DataStructures;
|
||||
|
||||
ByteQueue::ByteQueue()
|
||||
{
|
||||
readOffset=writeOffset=lengthAllocated=0;
|
||||
data=0;
|
||||
}
|
||||
ByteQueue::~ByteQueue()
|
||||
{
|
||||
Clear(_FILE_AND_LINE_);
|
||||
|
||||
|
||||
}
|
||||
void ByteQueue::WriteBytes(const char *in, unsigned length, const char *file, unsigned int line)
|
||||
{
|
||||
unsigned bytesWritten;
|
||||
bytesWritten=GetBytesWritten();
|
||||
if (lengthAllocated==0 || length > lengthAllocated-bytesWritten-1)
|
||||
{
|
||||
unsigned oldLengthAllocated=lengthAllocated;
|
||||
// Always need to waste 1 byte for the math to work, else writeoffset==readoffset
|
||||
unsigned newAmountToAllocate=length+oldLengthAllocated+1;
|
||||
if (newAmountToAllocate<256)
|
||||
newAmountToAllocate=256;
|
||||
lengthAllocated=lengthAllocated + newAmountToAllocate;
|
||||
data=(char*)rakRealloc_Ex(data, lengthAllocated, file, line);
|
||||
if (writeOffset < readOffset)
|
||||
{
|
||||
if (writeOffset <= newAmountToAllocate)
|
||||
{
|
||||
memcpy(data + oldLengthAllocated, data, writeOffset);
|
||||
writeOffset=readOffset+bytesWritten;
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(data + oldLengthAllocated, data, newAmountToAllocate);
|
||||
memmove(data, data+newAmountToAllocate, writeOffset-newAmountToAllocate);
|
||||
writeOffset-=newAmountToAllocate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (length <= lengthAllocated-writeOffset)
|
||||
memcpy(data+writeOffset, in, length);
|
||||
else
|
||||
{
|
||||
// Wrap
|
||||
memcpy(data+writeOffset, in, lengthAllocated-writeOffset);
|
||||
memcpy(data, in+(lengthAllocated-writeOffset), length-(lengthAllocated-writeOffset));
|
||||
}
|
||||
writeOffset=(writeOffset+length) % lengthAllocated;
|
||||
}
|
||||
bool ByteQueue::ReadBytes(char *out, unsigned maxLengthToRead, bool peek)
|
||||
{
|
||||
unsigned bytesWritten = GetBytesWritten();
|
||||
unsigned bytesToRead = bytesWritten < maxLengthToRead ? bytesWritten : maxLengthToRead;
|
||||
if (bytesToRead==0)
|
||||
return false;
|
||||
if (writeOffset>=readOffset)
|
||||
{
|
||||
memcpy(out, data+readOffset, bytesToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned availableUntilWrap = lengthAllocated-readOffset;
|
||||
if (bytesToRead <= availableUntilWrap)
|
||||
{
|
||||
memcpy(out, data+readOffset, bytesToRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(out, data+readOffset, availableUntilWrap);
|
||||
memcpy(out+availableUntilWrap, data, bytesToRead-availableUntilWrap);
|
||||
}
|
||||
}
|
||||
|
||||
if (peek==false)
|
||||
IncrementReadOffset(bytesToRead);
|
||||
|
||||
return true;
|
||||
}
|
||||
char* ByteQueue::PeekContiguousBytes(unsigned int *outLength) const
|
||||
{
|
||||
if (writeOffset>=readOffset)
|
||||
*outLength=writeOffset-readOffset;
|
||||
else
|
||||
*outLength=lengthAllocated-readOffset;
|
||||
return data+readOffset;
|
||||
}
|
||||
void ByteQueue::Clear(const char *file, unsigned int line)
|
||||
{
|
||||
if (lengthAllocated)
|
||||
rakFree_Ex(data, file, line );
|
||||
readOffset=writeOffset=lengthAllocated=0;
|
||||
data=0;
|
||||
}
|
||||
unsigned ByteQueue::GetBytesWritten(void) const
|
||||
{
|
||||
if (writeOffset>=readOffset)
|
||||
return writeOffset-readOffset;
|
||||
else
|
||||
return writeOffset+(lengthAllocated-readOffset);
|
||||
}
|
||||
void ByteQueue::IncrementReadOffset(unsigned length)
|
||||
{
|
||||
readOffset=(readOffset+length) % lengthAllocated;
|
||||
}
|
||||
void ByteQueue::DecrementReadOffset(unsigned length)
|
||||
{
|
||||
if (length>readOffset)
|
||||
readOffset=lengthAllocated-(length-readOffset);
|
||||
else
|
||||
readOffset-=length;
|
||||
}
|
||||
void ByteQueue::Print(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=readOffset; i!=writeOffset; i++)
|
||||
RAKNET_DEBUG_PRINTF("%i ", data[i]);
|
||||
RAKNET_DEBUG_PRINTF("\n");
|
||||
}
|
||||
40
third-party/RakNet/src/RakNet/DS_ByteQueue.hpp
vendored
Normal file
40
third-party/RakNet/src/RakNet/DS_ByteQueue.hpp
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/// \file DS_ByteQueue.h
|
||||
/// \internal
|
||||
/// \brief Byte queue
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __BYTE_QUEUE_H
|
||||
#define __BYTE_QUEUE_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
/// 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, const char *file, unsigned int line);
|
||||
bool ReadBytes(char *out, unsigned maxLengthToRead, bool peek);
|
||||
unsigned GetBytesWritten(void) const;
|
||||
char* PeekContiguousBytes(unsigned int *outLength) const;
|
||||
void IncrementReadOffset(unsigned length);
|
||||
void DecrementReadOffset(unsigned length);
|
||||
void Clear(const char *file, unsigned int line);
|
||||
void Print(void);
|
||||
|
||||
protected:
|
||||
char *data;
|
||||
unsigned readOffset, writeOffset, lengthAllocated;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
344
third-party/RakNet/src/RakNet/DS_Hash.hpp
vendored
Normal file
344
third-party/RakNet/src/RakNet/DS_Hash.hpp
vendored
Normal file
@@ -0,0 +1,344 @@
|
||||
/// \internal
|
||||
/// \brief Hashing container
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __HASH_H
|
||||
#define __HASH_H
|
||||
|
||||
#include "RakAssert.hpp"
|
||||
#include <string.h> // memmove
|
||||
#include "Export.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakString.hpp"
|
||||
|
||||
/// 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
|
||||
{
|
||||
struct HashIndex
|
||||
{
|
||||
unsigned int primaryIndex;
|
||||
unsigned int secondaryIndex;
|
||||
bool IsInvalid(void) const {return primaryIndex==(unsigned int) -1;}
|
||||
void SetInvalid(void) {primaryIndex=(unsigned int) -1; secondaryIndex=(unsigned int) -1;}
|
||||
};
|
||||
|
||||
/// \brief Using a string as a identifier for a node, store an allocated pointer to that node
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
class RAK_DLL_EXPORT Hash
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
Hash();
|
||||
|
||||
// Destructor
|
||||
~Hash();
|
||||
|
||||
void Push(key_type key, const data_type &input, const char *file, unsigned int line );
|
||||
data_type* Peek(key_type key );
|
||||
bool Pop(data_type& out, key_type key, const char *file, unsigned int line );
|
||||
bool RemoveAtIndex(HashIndex index, const char *file, unsigned int line );
|
||||
bool Remove(key_type key, const char *file, unsigned int line );
|
||||
HashIndex GetIndexOf(key_type key);
|
||||
bool HasData(key_type key);
|
||||
data_type& ItemAtIndex(const HashIndex &index);
|
||||
key_type KeyAtIndex(const HashIndex &index);
|
||||
void GetAsList(DataStructures::List<data_type> &itemList,DataStructures::List<key_type > &keyList,const char *file, unsigned int line) const;
|
||||
unsigned int Size(void) const;
|
||||
|
||||
/// \brief Clear the list
|
||||
void Clear( const char *file, unsigned int line );
|
||||
|
||||
struct Node
|
||||
{
|
||||
Node(key_type strIn, const data_type &_data) {string=strIn; data=_data;}
|
||||
key_type string;
|
||||
data_type data;
|
||||
// Next in the list for this key
|
||||
Node *next;
|
||||
};
|
||||
|
||||
protected:
|
||||
void ClearIndex(unsigned int index,const char *file, unsigned int line);
|
||||
Node **nodeList;
|
||||
unsigned int size;
|
||||
};
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
Hash<key_type, data_type, HASH_SIZE, hashFunction>::Hash()
|
||||
{
|
||||
nodeList=0;
|
||||
size=0;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
Hash<key_type, data_type, HASH_SIZE, hashFunction>::~Hash()
|
||||
{
|
||||
Clear(_FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::Push(key_type key, const data_type &input, const char *file, unsigned int line )
|
||||
{
|
||||
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
|
||||
if (nodeList==0)
|
||||
{
|
||||
nodeList=RakNet::OP_NEW_ARRAY<Node *>(HASH_SIZE,file,line);
|
||||
memset(nodeList,0,sizeof(Node *)*HASH_SIZE);
|
||||
}
|
||||
|
||||
Node *newNode=RakNet::OP_NEW_2<Node>(file,line,key,input);
|
||||
newNode->next=nodeList[hashIndex];
|
||||
nodeList[hashIndex]=newNode;
|
||||
|
||||
size++;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
data_type* Hash<key_type, data_type, HASH_SIZE, hashFunction>::Peek(key_type key )
|
||||
{
|
||||
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
|
||||
Node *node = nodeList[hashIndex];
|
||||
while (node!=0)
|
||||
{
|
||||
if (node->string==key)
|
||||
return &node->data;
|
||||
node=node->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::Pop(data_type& out, key_type key, const char *file, unsigned int line )
|
||||
{
|
||||
unsigned long hashIndex = (*hashFunction)(key) % HASH_SIZE;
|
||||
Node *node = nodeList[hashIndex];
|
||||
if (node==0)
|
||||
return false;
|
||||
if (node->next==0)
|
||||
{
|
||||
// Only one item.
|
||||
if (node->string==key)
|
||||
{
|
||||
// Delete last item
|
||||
out=node->data;
|
||||
ClearIndex(hashIndex,_FILE_AND_LINE_);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Single item doesn't match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (node->string==key)
|
||||
{
|
||||
// First item does match, but more than one item
|
||||
out=node->data;
|
||||
nodeList[hashIndex]=node->next;
|
||||
RakNet::OP_DELETE(node,file,line);
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
Node *last=node;
|
||||
node=node->next;
|
||||
|
||||
while (node!=0)
|
||||
{
|
||||
// First item does not match, but subsequent item might
|
||||
if (node->string==key)
|
||||
{
|
||||
out=node->data;
|
||||
// Skip over subsequent item
|
||||
last->next=node->next;
|
||||
// Delete existing item
|
||||
RakNet::OP_DELETE(node,file,line);
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
last=node;
|
||||
node=node->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::RemoveAtIndex(HashIndex index, const char *file, unsigned int line )
|
||||
{
|
||||
if (index.IsInvalid())
|
||||
return false;
|
||||
|
||||
Node *node = nodeList[index.primaryIndex];
|
||||
if (node==0)
|
||||
return false;
|
||||
if (node->next==0)
|
||||
{
|
||||
// Delete last item
|
||||
ClearIndex(index.primaryIndex,file,line);
|
||||
return true;
|
||||
}
|
||||
else if (index.secondaryIndex==0)
|
||||
{
|
||||
// First item does match, but more than one item
|
||||
nodeList[index.primaryIndex]=node->next;
|
||||
RakNet::OP_DELETE(node,file,line);
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
Node *last=node;
|
||||
node=node->next;
|
||||
--index.secondaryIndex;
|
||||
|
||||
while (index.secondaryIndex!=0)
|
||||
{
|
||||
last=node;
|
||||
node=node->next;
|
||||
--index.secondaryIndex;
|
||||
}
|
||||
|
||||
// Skip over subsequent item
|
||||
last->next=node->next;
|
||||
// Delete existing item
|
||||
RakNet::OP_DELETE(node,file,line);
|
||||
size--;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::Remove(key_type key, const char *file, unsigned int line )
|
||||
{
|
||||
return RemoveAtIndex(GetIndexOf(key),file,line);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
HashIndex Hash<key_type, data_type, HASH_SIZE, hashFunction>::GetIndexOf(key_type key)
|
||||
{
|
||||
if (nodeList==0)
|
||||
{
|
||||
HashIndex temp;
|
||||
temp.SetInvalid();
|
||||
return temp;
|
||||
}
|
||||
HashIndex idx;
|
||||
idx.primaryIndex=(*hashFunction)(key) % HASH_SIZE;
|
||||
Node *node = nodeList[idx.primaryIndex];
|
||||
if (node==0)
|
||||
{
|
||||
idx.SetInvalid();
|
||||
return idx;
|
||||
}
|
||||
idx.secondaryIndex=0;
|
||||
while (node!=0)
|
||||
{
|
||||
if (node->string==key)
|
||||
{
|
||||
return idx;
|
||||
}
|
||||
node=node->next;
|
||||
idx.secondaryIndex++;
|
||||
}
|
||||
|
||||
idx.SetInvalid();
|
||||
return idx;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
bool Hash<key_type, data_type, HASH_SIZE, hashFunction>::HasData(key_type key)
|
||||
{
|
||||
return GetIndexOf(key).IsInvalid()==false;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
data_type& Hash<key_type, data_type, HASH_SIZE, hashFunction>::ItemAtIndex(const HashIndex &index)
|
||||
{
|
||||
Node *node = nodeList[index.primaryIndex];
|
||||
RakAssert(node);
|
||||
unsigned int i;
|
||||
for (i=0; i < index.secondaryIndex; i++)
|
||||
{
|
||||
node=node->next;
|
||||
RakAssert(node);
|
||||
}
|
||||
return node->data;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
key_type Hash<key_type, data_type, HASH_SIZE, hashFunction>::KeyAtIndex(const HashIndex &index)
|
||||
{
|
||||
Node *node = nodeList[index.primaryIndex];
|
||||
RakAssert(node);
|
||||
unsigned int i;
|
||||
for (i=0; i < index.secondaryIndex; i++)
|
||||
{
|
||||
node=node->next;
|
||||
RakAssert(node);
|
||||
}
|
||||
return node->string;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::Clear(const char *file, unsigned int line)
|
||||
{
|
||||
if (nodeList)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i=0; i < HASH_SIZE; i++)
|
||||
ClearIndex(i,file,line);
|
||||
RakNet::OP_DELETE_ARRAY(nodeList,file,line);
|
||||
nodeList=0;
|
||||
size=0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::ClearIndex(unsigned int index,const char *file, unsigned int line)
|
||||
{
|
||||
Node *node = nodeList[index];
|
||||
Node *next;
|
||||
while (node)
|
||||
{
|
||||
next=node->next;
|
||||
RakNet::OP_DELETE(node,file,line);
|
||||
node=next;
|
||||
size--;
|
||||
}
|
||||
nodeList[index]=0;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
void Hash<key_type, data_type, HASH_SIZE, hashFunction>::GetAsList(DataStructures::List<data_type> &itemList,DataStructures::List<key_type > &keyList,const char *file, unsigned int line) const
|
||||
{
|
||||
if (nodeList==0)
|
||||
return;
|
||||
itemList.Clear(false,_FILE_AND_LINE_);
|
||||
keyList.Clear(false,_FILE_AND_LINE_);
|
||||
|
||||
Node *node;
|
||||
unsigned int i;
|
||||
for (i=0; i < HASH_SIZE; i++)
|
||||
{
|
||||
if (nodeList[i])
|
||||
{
|
||||
node=nodeList[i];
|
||||
while (node)
|
||||
{
|
||||
itemList.Push(node->data,file,line);
|
||||
keyList.Push(node->string,file,line);
|
||||
node=node->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
template <class key_type, class data_type, unsigned int HASH_SIZE, unsigned long (*hashFunction)(const key_type &) >
|
||||
unsigned int Hash<key_type, data_type, HASH_SIZE, hashFunction>::Size(void) const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
297
third-party/RakNet/src/RakNet/DS_Heap.hpp
vendored
Normal file
297
third-party/RakNet/src/RakNet/DS_Heap.hpp
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
/// \file DS_Heap.h
|
||||
/// \internal
|
||||
/// \brief Heap (Also serves as a priority queue)
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __RAKNET_HEAP_H
|
||||
#define __RAKNET_HEAP_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
class RAK_DLL_EXPORT Heap
|
||||
{
|
||||
public:
|
||||
struct HeapNode
|
||||
{
|
||||
HeapNode() {}
|
||||
HeapNode(const weight_type &w, const data_type &d) : weight(w), data(d) {}
|
||||
weight_type weight; // I'm assuming key is a native numerical type - float or int
|
||||
data_type data;
|
||||
};
|
||||
|
||||
Heap();
|
||||
~Heap();
|
||||
void Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line);
|
||||
/// Call before calling PushSeries, for a new series of items
|
||||
void StartSeries(void) {optimizeNextSeriesPush=false;}
|
||||
/// If you are going to push a list of items, where the weights of the items on the list are in order and follow the heap order, PushSeries is faster than Push()
|
||||
void PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line);
|
||||
data_type Pop(const unsigned startingIndex);
|
||||
data_type Peek(const unsigned startingIndex=0) const;
|
||||
weight_type PeekWeight(const unsigned startingIndex=0) const;
|
||||
void Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line);
|
||||
data_type& operator[] ( const unsigned int position ) const;
|
||||
unsigned Size(void) const;
|
||||
|
||||
protected:
|
||||
unsigned LeftChild(const unsigned i) const;
|
||||
unsigned RightChild(const unsigned i) const;
|
||||
unsigned Parent(const unsigned i) const;
|
||||
void Swap(const unsigned i, const unsigned j);
|
||||
DataStructures::List<HeapNode> heap;
|
||||
bool optimizeNextSeriesPush;
|
||||
};
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
Heap<weight_type, data_type, isMaxHeap>::Heap()
|
||||
{
|
||||
optimizeNextSeriesPush=false;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
Heap<weight_type, data_type, isMaxHeap>::~Heap()
|
||||
{
|
||||
//Clear(true, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
void Heap<weight_type, data_type, isMaxHeap>::PushSeries(const weight_type &weight, const data_type &data, const char *file, unsigned int line)
|
||||
{
|
||||
if (optimizeNextSeriesPush==false)
|
||||
{
|
||||
// If the weight of what we are inserting is greater than / less than in order of the heap of every sibling and sibling of parent, then can optimize next push
|
||||
unsigned currentIndex = heap.Size();
|
||||
unsigned parentIndex;
|
||||
if (currentIndex>0)
|
||||
{
|
||||
for (parentIndex = Parent(currentIndex); parentIndex < currentIndex; parentIndex++)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#endif
|
||||
if (isMaxHeap)
|
||||
{
|
||||
// Every child is less than its parent
|
||||
if (weight>heap[parentIndex].weight)
|
||||
{
|
||||
// Can't optimize
|
||||
Push(weight,data,file,line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Every child is greater than than its parent
|
||||
if (weight<heap[parentIndex].weight)
|
||||
{
|
||||
// Can't optimize
|
||||
Push(weight,data,file,line);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parent's subsequent siblings and this row's siblings all are less than / greater than inserted element. Can insert all further elements straight to the end
|
||||
heap.Insert(HeapNode(weight, data), file, line);
|
||||
optimizeNextSeriesPush=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
heap.Insert(HeapNode(weight, data), file, line);
|
||||
}
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
void Heap<weight_type, data_type, isMaxHeap>::Push(const weight_type &weight, const data_type &data, const char *file, unsigned int line)
|
||||
{
|
||||
unsigned currentIndex = heap.Size();
|
||||
unsigned parentIndex;
|
||||
heap.Insert(HeapNode(weight, data), file, line);
|
||||
while (currentIndex!=0)
|
||||
{
|
||||
parentIndex = Parent(currentIndex);
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
if (isMaxHeap)
|
||||
{
|
||||
if (heap[parentIndex].weight < weight)
|
||||
{
|
||||
Swap(currentIndex, parentIndex);
|
||||
currentIndex=parentIndex;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (heap[parentIndex].weight > weight)
|
||||
{
|
||||
Swap(currentIndex, parentIndex);
|
||||
currentIndex=parentIndex;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
data_type Heap<weight_type, data_type, isMaxHeap>::Pop(const unsigned startingIndex)
|
||||
{
|
||||
// While we have children, swap out with the larger of the two children.
|
||||
|
||||
// This line will assert on an empty heap
|
||||
data_type returnValue=heap[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 <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline data_type Heap<weight_type, data_type, isMaxHeap>::Peek(const unsigned startingIndex) const
|
||||
{
|
||||
return heap[startingIndex].data;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline weight_type Heap<weight_type, data_type, isMaxHeap>::PeekWeight(const unsigned startingIndex) const
|
||||
{
|
||||
return heap[startingIndex].weight;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
void Heap<weight_type, data_type, isMaxHeap>::Clear(bool doNotDeallocateSmallBlocks, const char *file, unsigned int line)
|
||||
{
|
||||
heap.Clear(doNotDeallocateSmallBlocks, file, line);
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline data_type& Heap<weight_type, data_type, isMaxHeap>::operator[] ( const unsigned int position ) const
|
||||
{
|
||||
return heap[position].data;
|
||||
}
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
unsigned Heap<weight_type, data_type, isMaxHeap>::Size(void) const
|
||||
{
|
||||
return heap.Size();
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline unsigned Heap<weight_type, data_type, isMaxHeap>::LeftChild(const unsigned i) const
|
||||
{
|
||||
return i*2+1;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline unsigned Heap<weight_type, data_type, isMaxHeap>::RightChild(const unsigned i) const
|
||||
{
|
||||
return i*2+2;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
inline unsigned Heap<weight_type, data_type, isMaxHeap>::Parent(const unsigned i) const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(i!=0);
|
||||
#endif
|
||||
return (i-1)/2;
|
||||
}
|
||||
|
||||
template <class weight_type, class data_type, bool isMaxHeap>
|
||||
void Heap<weight_type, data_type, isMaxHeap>::Swap(const unsigned i, const unsigned j)
|
||||
{
|
||||
HeapNode temp;
|
||||
temp=heap[i];
|
||||
heap[i]=heap[j];
|
||||
heap[j]=temp;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
299
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTree.cpp
vendored
Normal file
299
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTree.cpp
vendored
Normal file
@@ -0,0 +1,299 @@
|
||||
/// \file
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#include "DS_HuffmanEncodingTree.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
HuffmanEncodingTree::HuffmanEncodingTree()
|
||||
{
|
||||
root = 0;
|
||||
}
|
||||
|
||||
HuffmanEncodingTree::~HuffmanEncodingTree()
|
||||
{
|
||||
FreeMemory();
|
||||
}
|
||||
|
||||
void HuffmanEncodingTree::FreeMemory( void )
|
||||
{
|
||||
if ( root == 0 )
|
||||
return ;
|
||||
|
||||
// Use an in-order traversal to delete the tree
|
||||
DataStructures::Queue<HuffmanEncodingTreeNode *> nodeQueue;
|
||||
|
||||
HuffmanEncodingTreeNode *node;
|
||||
|
||||
nodeQueue.Push( root, _FILE_AND_LINE_ );
|
||||
|
||||
while ( nodeQueue.Size() > 0 )
|
||||
{
|
||||
node = nodeQueue.Pop();
|
||||
|
||||
if ( node->left )
|
||||
nodeQueue.Push( node->left, _FILE_AND_LINE_ );
|
||||
|
||||
if ( node->right )
|
||||
nodeQueue.Push( node->right, _FILE_AND_LINE_ );
|
||||
|
||||
RakNet::OP_DELETE(node, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
// Delete the encoding table
|
||||
for ( int i = 0; i < 256; i++ )
|
||||
rakFree_Ex(encodingTable[ i ].encoding, _FILE_AND_LINE_ );
|
||||
|
||||
root = 0;
|
||||
}
|
||||
|
||||
|
||||
////#include <stdio.h>
|
||||
|
||||
// Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree
|
||||
void HuffmanEncodingTree::GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] )
|
||||
{
|
||||
int counter;
|
||||
HuffmanEncodingTreeNode * node;
|
||||
HuffmanEncodingTreeNode *leafList[ 256 ]; // Keep a copy of the pointers to all the leaves so we can generate the encryption table bottom-up, which is easier
|
||||
// 1. Make 256 trees each with a weight equal to the frequency of the corresponding character
|
||||
DataStructures::LinkedList<HuffmanEncodingTreeNode *> huffmanEncodingTreeNodeList;
|
||||
|
||||
FreeMemory();
|
||||
|
||||
for ( counter = 0; counter < 256; counter++ )
|
||||
{
|
||||
node = RakNet::OP_NEW<HuffmanEncodingTreeNode>( _FILE_AND_LINE_ );
|
||||
node->left = 0;
|
||||
node->right = 0;
|
||||
node->value = (unsigned char) counter;
|
||||
node->weight = frequencyTable[ counter ];
|
||||
|
||||
if ( node->weight == 0 )
|
||||
node->weight = 1; // 0 weights are illegal
|
||||
|
||||
leafList[ counter ] = node; // Used later to generate the encryption table
|
||||
|
||||
InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList ); // Insert and maintain sort order.
|
||||
}
|
||||
|
||||
|
||||
// 2. While there is more than one tree, take the two smallest trees and merge them so that the two trees are the left and right
|
||||
// children of a new node, where the new node has the weight the sum of the weight of the left and right child nodes.
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
while ( 1 )
|
||||
{
|
||||
huffmanEncodingTreeNodeList.Beginning();
|
||||
HuffmanEncodingTreeNode *lesser, *greater;
|
||||
lesser = huffmanEncodingTreeNodeList.Pop();
|
||||
greater = huffmanEncodingTreeNodeList.Pop();
|
||||
node = RakNet::OP_NEW<HuffmanEncodingTreeNode>( _FILE_AND_LINE_ );
|
||||
node->left = lesser;
|
||||
node->right = greater;
|
||||
node->weight = lesser->weight + greater->weight;
|
||||
lesser->parent = node; // This is done to make generating the encryption table easier
|
||||
greater->parent = node; // This is done to make generating the encryption table easier
|
||||
|
||||
if ( huffmanEncodingTreeNodeList.Size() == 0 )
|
||||
{
|
||||
// 3. Assign the one remaining node in the list to the root node.
|
||||
root = node;
|
||||
root->parent = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// Put the new node back into the list at the correct spot to maintain the sort. Linear search time
|
||||
InsertNodeIntoSortedList( node, &huffmanEncodingTreeNodeList );
|
||||
}
|
||||
|
||||
bool tempPath[ 256 ]; // Maximum path length is 256
|
||||
unsigned short tempPathLength;
|
||||
HuffmanEncodingTreeNode *currentNode;
|
||||
RakNet::BitStream bitStream;
|
||||
|
||||
// Generate the encryption table. From before, we have an array of pointers to all the leaves which contain pointers to their parents.
|
||||
// This can be done more efficiently but this isn't bad and it's way easier to program and debug
|
||||
|
||||
for ( counter = 0; counter < 256; counter++ )
|
||||
{
|
||||
// Already done at the end of the loop and before it!
|
||||
tempPathLength = 0;
|
||||
|
||||
// Set the current node at the leaf
|
||||
currentNode = leafList[ counter ];
|
||||
|
||||
do
|
||||
{
|
||||
if ( currentNode->parent->left == currentNode ) // We're storing the paths in reverse order.since we are going from the leaf to the root
|
||||
tempPath[ tempPathLength++ ] = false;
|
||||
else
|
||||
tempPath[ tempPathLength++ ] = true;
|
||||
|
||||
currentNode = currentNode->parent;
|
||||
}
|
||||
|
||||
while ( currentNode != root );
|
||||
|
||||
// Write to the bitstream in the reverse order that we stored the path, which gives us the correct order from the root to the leaf
|
||||
while ( tempPathLength-- > 0 )
|
||||
{
|
||||
if ( tempPath[ tempPathLength ] ) // Write 1's and 0's because writing a bool will write the BitStream TYPE_CHECKING validation bits if that is defined along with the actual data bit, which is not what we want
|
||||
bitStream.Write1();
|
||||
else
|
||||
bitStream.Write0();
|
||||
}
|
||||
|
||||
// Read data from the bitstream, which is written to the encoding table in bits and bitlength. Note this function allocates the encodingTable[counter].encoding pointer
|
||||
encodingTable[ counter ].bitLength = ( unsigned char ) bitStream.CopyData( &encodingTable[ counter ].encoding );
|
||||
|
||||
// Reset the bitstream for the next iteration
|
||||
bitStream.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass an array of bytes to array and a preallocated BitStream to receive the output
|
||||
void HuffmanEncodingTree::EncodeArray( unsigned char *input, size_t sizeInBytes, RakNet::BitStream * output )
|
||||
{
|
||||
unsigned counter;
|
||||
|
||||
// For each input byte, Write out the corresponding series of 1's and 0's that give the encoded representation
|
||||
for ( counter = 0; counter < sizeInBytes; counter++ )
|
||||
{
|
||||
output->WriteBits( encodingTable[ input[ counter ] ].encoding, encodingTable[ input[ counter ] ].bitLength, false ); // Data is left aligned
|
||||
}
|
||||
|
||||
// Byte align the output so the unassigned remaining bits don't equate to some actual value
|
||||
if ( output->GetNumberOfBitsUsed() % 8 != 0 )
|
||||
{
|
||||
// Find an input that is longer than the remaining bits. Write out part of it to pad the output to be byte aligned.
|
||||
unsigned char remainingBits = (unsigned char) ( 8 - ( output->GetNumberOfBitsUsed() % 8 ) );
|
||||
|
||||
for ( counter = 0; counter < 256; counter++ )
|
||||
if ( encodingTable[ counter ].bitLength > remainingBits )
|
||||
{
|
||||
output->WriteBits( encodingTable[ counter ].encoding, remainingBits, false ); // Data is left aligned
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
RakAssert( counter != 256 ); // Given 256 elements, we should always be able to find an input that would be >= 7 bits
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
unsigned HuffmanEncodingTree::DecodeArray( RakNet::BitStream * input, BitSize_t sizeInBits, size_t maxCharsToWrite, unsigned char *output )
|
||||
{
|
||||
HuffmanEncodingTreeNode * currentNode;
|
||||
|
||||
unsigned outputWriteIndex;
|
||||
outputWriteIndex = 0;
|
||||
currentNode = root;
|
||||
|
||||
// For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root
|
||||
|
||||
for ( unsigned counter = 0; counter < sizeInBits; counter++ )
|
||||
{
|
||||
if ( input->ReadBit() == false ) // left!
|
||||
currentNode = currentNode->left;
|
||||
else
|
||||
currentNode = currentNode->right;
|
||||
|
||||
if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf
|
||||
{
|
||||
|
||||
if ( outputWriteIndex < maxCharsToWrite )
|
||||
output[ outputWriteIndex ] = currentNode->value;
|
||||
|
||||
outputWriteIndex++;
|
||||
|
||||
currentNode = root;
|
||||
}
|
||||
}
|
||||
|
||||
return outputWriteIndex;
|
||||
}
|
||||
|
||||
// Pass an array of encoded bytes to array and a preallocated BitStream to receive the output
|
||||
void HuffmanEncodingTree::DecodeArray( unsigned char *input, BitSize_t sizeInBits, RakNet::BitStream * output )
|
||||
{
|
||||
HuffmanEncodingTreeNode * currentNode;
|
||||
|
||||
if ( sizeInBits <= 0 )
|
||||
return ;
|
||||
|
||||
RakNet::BitStream bitStream( input, BITS_TO_BYTES(sizeInBits), false );
|
||||
|
||||
currentNode = root;
|
||||
|
||||
// For each bit, go left if it is a 0 and right if it is a 1. When we reach a leaf, that gives us the desired value and we restart from the root
|
||||
for ( unsigned counter = 0; counter < sizeInBits; counter++ )
|
||||
{
|
||||
if ( bitStream.ReadBit() == false ) // left!
|
||||
currentNode = currentNode->left;
|
||||
else
|
||||
currentNode = currentNode->right;
|
||||
|
||||
if ( currentNode->left == 0 && currentNode->right == 0 ) // Leaf
|
||||
{
|
||||
output->WriteBits( &( currentNode->value ), sizeof( char ) * 8, true ); // Use WriteBits instead of Write(char) because we want to avoid TYPE_CHECKING
|
||||
currentNode = root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insertion sort. Slow but easy to write in this case
|
||||
void HuffmanEncodingTree::InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList<HuffmanEncodingTreeNode *> *huffmanEncodingTreeNodeList ) const
|
||||
{
|
||||
if ( huffmanEncodingTreeNodeList->Size() == 0 )
|
||||
{
|
||||
huffmanEncodingTreeNodeList->Insert( node );
|
||||
return ;
|
||||
}
|
||||
|
||||
huffmanEncodingTreeNodeList->Beginning();
|
||||
|
||||
unsigned counter = 0;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
while ( 1 )
|
||||
{
|
||||
if ( huffmanEncodingTreeNodeList->Peek()->weight < node->weight )
|
||||
++( *huffmanEncodingTreeNodeList );
|
||||
else
|
||||
{
|
||||
huffmanEncodingTreeNodeList->Insert( node );
|
||||
break;
|
||||
}
|
||||
|
||||
// Didn't find a spot in the middle - add to the end
|
||||
if ( ++counter == huffmanEncodingTreeNodeList->Size() )
|
||||
{
|
||||
huffmanEncodingTreeNodeList->End();
|
||||
|
||||
huffmanEncodingTreeNodeList->Add( node )
|
||||
|
||||
; // Add to the end
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
67
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTree.hpp
vendored
Normal file
67
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTree.hpp
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/// \file DS_HuffmanEncodingTree.h
|
||||
/// \brief \b [Internal] Generates a huffman encoding tree, used for string and global compression.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __HUFFMAN_ENCODING_TREE
|
||||
#define __HUFFMAN_ENCODING_TREE
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_HuffmanEncodingTreeNode.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "DS_LinkedList.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
/// 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();
|
||||
|
||||
/// \brief 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 );
|
||||
|
||||
// \brief 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 );
|
||||
|
||||
/// \brief Given a frequency table of 256 elements, all with a frequency of 1 or more, generate the tree.
|
||||
void GenerateFromFrequencyTable( unsigned int frequencyTable[ 256 ] );
|
||||
|
||||
/// \brief Free the memory used by the tree.
|
||||
void FreeMemory( void );
|
||||
|
||||
private:
|
||||
|
||||
/// The root node of the tree
|
||||
|
||||
HuffmanEncodingTreeNode *root;
|
||||
|
||||
/// Used to hold bit encoding for one character
|
||||
|
||||
|
||||
struct CharacterEncoding
|
||||
{
|
||||
unsigned char* encoding;
|
||||
unsigned short bitLength;
|
||||
};
|
||||
|
||||
CharacterEncoding encodingTable[ 256 ];
|
||||
|
||||
void InsertNodeIntoSortedList( HuffmanEncodingTreeNode * node, DataStructures::LinkedList<HuffmanEncodingTreeNode *> *huffmanEncodingTreeNodeList ) const;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
57
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTreeFactory.hpp
vendored
Normal file
57
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTreeFactory.hpp
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/// \file DS_HuffmanEncodingTreeFactory.h
|
||||
/// \internal
|
||||
/// \brief Creates instances of the class HuffmanEncodingTree
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __HUFFMAN_ENCODING_TREE_FACTORY
|
||||
#define __HUFFMAN_ENCODING_TREE_FACTORY
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
namespace RakNet {
|
||||
/// Forward declarations
|
||||
class HuffmanEncodingTree;
|
||||
|
||||
/// \brief Creates instances of the class HuffmanEncodingTree
|
||||
/// \details This class takes a frequency table and given that frequence table, will generate an instance of HuffmanEncodingTree
|
||||
class HuffmanEncodingTreeFactory
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
HuffmanEncodingTreeFactory();
|
||||
|
||||
/// \brief Reset the frequency table.
|
||||
/// \details You don't need to call this unless you want to reuse the class for a new tree
|
||||
void Reset( void );
|
||||
|
||||
/// \brief 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 );
|
||||
|
||||
/// \brief 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 ] );
|
||||
|
||||
/// \brief Returns the frequency table as a pointer.
|
||||
/// \return the address of the frenquency table
|
||||
unsigned int * GetFrequencyTable( void );
|
||||
|
||||
/// \brief Generate a HuffmanEncodingTree.
|
||||
/// \details 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 ];
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
21
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTreeNode.hpp
vendored
Normal file
21
third-party/RakNet/src/RakNet/DS_HuffmanEncodingTreeNode.hpp
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
/// \file
|
||||
/// \brief \b [Internal] A single node in the Huffman Encoding Tree.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __HUFFMAN_ENCODING_TREE_NODE
|
||||
#define __HUFFMAN_ENCODING_TREE_NODE
|
||||
|
||||
struct HuffmanEncodingTreeNode
|
||||
{
|
||||
unsigned char value;
|
||||
unsigned weight;
|
||||
HuffmanEncodingTreeNode *left;
|
||||
HuffmanEncodingTreeNode *right;
|
||||
HuffmanEncodingTreeNode *parent;
|
||||
};
|
||||
|
||||
#endif
|
||||
1252
third-party/RakNet/src/RakNet/DS_LinkedList.hpp
vendored
Normal file
1252
third-party/RakNet/src/RakNet/DS_LinkedList.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
518
third-party/RakNet/src/RakNet/DS_List.hpp
vendored
Normal file
518
third-party/RakNet/src/RakNet/DS_List.hpp
vendored
Normal file
@@ -0,0 +1,518 @@
|
||||
/// \file DS_List.h
|
||||
/// \internal
|
||||
/// \brief Array based list.
|
||||
/// \details 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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __LIST_H
|
||||
#define __LIST_H
|
||||
|
||||
#include "RakAssert.hpp"
|
||||
#include <string.h> // memmove
|
||||
#include "Export.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
/// Maximum unsigned long
|
||||
static const unsigned int MAX_UNSIGNED_LONG = 4294967295U;
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
/// \brief Array based implementation of a list.
|
||||
/// \note ONLY USE THIS FOR SHALLOW COPIES. I don't bother with operator= to improve performance.
|
||||
template <class list_type>
|
||||
class RAK_DLL_EXPORT List
|
||||
{
|
||||
public:
|
||||
/// Default constructor
|
||||
List();
|
||||
|
||||
// Destructor
|
||||
~List();
|
||||
|
||||
/// \brief Copy constructor.
|
||||
/// \param[in] original_copy The list to duplicate
|
||||
List( const List& original_copy );
|
||||
|
||||
/// \brief Assign one list to another.
|
||||
List& operator= ( const List& original_copy );
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief Push an element at the end of the stack.
|
||||
/// \param[in] input The new element.
|
||||
void Push(const list_type &input, const char *file, unsigned int line );
|
||||
|
||||
/// \brief Pop an element from the end of the stack.
|
||||
/// \pre Size()>0
|
||||
/// \return The element at the end.
|
||||
list_type& Pop(void);
|
||||
|
||||
/// \brief 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, const char *file, unsigned int line );
|
||||
|
||||
/// \brief Insert at the end of the list.
|
||||
/// \param[in] input The new element.
|
||||
void Insert( const list_type &input, const char *file, unsigned int line );
|
||||
|
||||
/// \brief Replace the value at \a position by \a input.
|
||||
/// \details 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, const char *file, unsigned int line );
|
||||
|
||||
/// \brief 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 );
|
||||
|
||||
/// \brief Delete the element at position \a position.
|
||||
/// \param[in] position The index of the element to delete
|
||||
void RemoveAtIndex( const unsigned int position );
|
||||
|
||||
/// \brief 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 );
|
||||
|
||||
/// \brief Delete the element at the end of the list.
|
||||
void RemoveFromEnd(const unsigned num=1);
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief Clear the list
|
||||
void Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line );
|
||||
|
||||
/// \brief Preallocate the list, so it needs fewer reallocations at runtime.
|
||||
void Preallocate( unsigned countNeeded, const char *file, unsigned int line );
|
||||
|
||||
/// \brief Frees overallocated members, to use the minimum memory necessary.
|
||||
/// \attention
|
||||
/// This is a slow operation
|
||||
void Compress( const char *file, unsigned int line );
|
||||
|
||||
private:
|
||||
/// An array of user values
|
||||
list_type* listArray;
|
||||
|
||||
/// Number of elements in the list
|
||||
unsigned int list_size;
|
||||
|
||||
/// Size of \a array
|
||||
unsigned int allocation_size;
|
||||
};
|
||||
template <class list_type>
|
||||
List<list_type>::List()
|
||||
{
|
||||
allocation_size = 0;
|
||||
listArray = 0;
|
||||
list_size = 0;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
List<list_type>::~List()
|
||||
{
|
||||
if (allocation_size>0)
|
||||
RakNet::OP_DELETE_ARRAY(listArray, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
|
||||
template <class list_type>
|
||||
List<list_type>::List( const List& original_copy )
|
||||
{
|
||||
// Allocate memory for copy
|
||||
|
||||
if ( original_copy.list_size == 0 )
|
||||
{
|
||||
list_size = 0;
|
||||
allocation_size = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
listArray = RakNet::OP_NEW_ARRAY<list_type >( original_copy.list_size , _FILE_AND_LINE_ );
|
||||
|
||||
for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter )
|
||||
listArray[ counter ] = original_copy.listArray[ counter ];
|
||||
|
||||
// Don't call constructors, assignment operators, etc.
|
||||
//memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type));
|
||||
|
||||
list_size = allocation_size = original_copy.list_size;
|
||||
}
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
List<list_type>& List<list_type>::operator= ( const List& original_copy )
|
||||
{
|
||||
if ( ( &original_copy ) != this )
|
||||
{
|
||||
Clear( false, _FILE_AND_LINE_ );
|
||||
|
||||
// Allocate memory for copy
|
||||
|
||||
if ( original_copy.list_size == 0 )
|
||||
{
|
||||
list_size = 0;
|
||||
allocation_size = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
listArray = RakNet::OP_NEW_ARRAY<list_type >( original_copy.list_size , _FILE_AND_LINE_ );
|
||||
|
||||
for ( unsigned int counter = 0; counter < original_copy.list_size; ++counter )
|
||||
listArray[ counter ] = original_copy.listArray[ counter ];
|
||||
// Don't call constructors, assignment operators, etc.
|
||||
//memcpy(listArray, original_copy.listArray, original_copy.list_size*sizeof(list_type));
|
||||
|
||||
list_size = allocation_size = original_copy.list_size;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template <class list_type>
|
||||
inline list_type& List<list_type>::operator[] ( const unsigned int position ) const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (position>=list_size)
|
||||
{
|
||||
RakAssert ( position < list_size );
|
||||
}
|
||||
#endif
|
||||
return listArray[ position ];
|
||||
}
|
||||
|
||||
// Just here for debugging
|
||||
template <class list_type>
|
||||
inline list_type& List<list_type>::Get ( const unsigned int position ) const
|
||||
{
|
||||
return listArray[ position ];
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Push(const list_type &input, const char *file, unsigned int line)
|
||||
{
|
||||
Insert(input, file, line);
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
inline list_type& List<list_type>::Pop(void)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(list_size>0);
|
||||
#endif
|
||||
--list_size;
|
||||
return listArray[list_size];
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Insert( const list_type &input, const unsigned int position, const char *file, unsigned int line )
|
||||
{
|
||||
#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<list_type >( allocation_size , file, line );
|
||||
|
||||
// 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, file, line);
|
||||
|
||||
listArray = new_array;
|
||||
}
|
||||
|
||||
// Move the elements in the list to make room
|
||||
for ( unsigned int counter = list_size; counter != position; counter-- )
|
||||
listArray[ counter ] = listArray[ counter - 1 ];
|
||||
|
||||
// Don't call constructors, assignment operators, etc.
|
||||
//memmove(listArray+position+1, listArray+position, (list_size-position)*sizeof(list_type));
|
||||
|
||||
// Insert the new item at the correct spot
|
||||
listArray[ position ] = input;
|
||||
|
||||
++list_size;
|
||||
|
||||
}
|
||||
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Insert( const list_type &input, const char *file, unsigned int line )
|
||||
{
|
||||
// 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<list_type >( allocation_size , file, line );
|
||||
|
||||
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, file, line);
|
||||
}
|
||||
|
||||
listArray = new_array;
|
||||
}
|
||||
|
||||
// Insert the new item at the correct spot
|
||||
listArray[ list_size ] = input;
|
||||
|
||||
++list_size;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
inline void List<list_type>::Replace( const list_type &input, const list_type filler, const unsigned int position, const char *file, unsigned int line )
|
||||
{
|
||||
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<list_type >( allocation_size , file, line );
|
||||
|
||||
// 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, file, line);
|
||||
|
||||
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 <class list_type>
|
||||
inline void List<list_type>::Replace( const list_type &input )
|
||||
{
|
||||
if ( list_size > 0 )
|
||||
listArray[ list_size - 1 ] = input;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::RemoveAtIndex( const unsigned int position )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (position >= list_size)
|
||||
{
|
||||
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 <class list_type>
|
||||
void List<list_type>::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 <class list_type>
|
||||
inline void List<list_type>::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 <class list_type>
|
||||
unsigned int List<list_type>::GetIndexOf( const list_type &input ) const
|
||||
{
|
||||
for ( unsigned int i = 0; i < list_size; ++i )
|
||||
if ( listArray[ i ] == input )
|
||||
return i;
|
||||
|
||||
return MAX_UNSIGNED_LONG;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
inline unsigned int List<list_type>::Size( void ) const
|
||||
{
|
||||
return list_size;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Clear( bool doNotDeallocateSmallBlocks, const char *file, unsigned int line )
|
||||
{
|
||||
if ( allocation_size == 0 )
|
||||
return;
|
||||
|
||||
if (allocation_size>512 || doNotDeallocateSmallBlocks==false)
|
||||
{
|
||||
RakNet::OP_DELETE_ARRAY(listArray, file, line);
|
||||
allocation_size = 0;
|
||||
listArray = 0;
|
||||
}
|
||||
list_size = 0;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Compress( const char *file, unsigned int line )
|
||||
{
|
||||
list_type * new_array;
|
||||
|
||||
if ( allocation_size == 0 )
|
||||
return ;
|
||||
|
||||
new_array = RakNet::OP_NEW_ARRAY<list_type >( allocation_size , file, line );
|
||||
|
||||
// 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, file, line);
|
||||
|
||||
listArray = new_array;
|
||||
}
|
||||
|
||||
template <class list_type>
|
||||
void List<list_type>::Preallocate( unsigned countNeeded, const char *file, unsigned int line )
|
||||
{
|
||||
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 , file, line );
|
||||
|
||||
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, file, line);
|
||||
}
|
||||
|
||||
listArray = new_array;
|
||||
}
|
||||
}
|
||||
|
||||
} // End namespace
|
||||
|
||||
#endif
|
||||
321
third-party/RakNet/src/RakNet/DS_Map.hpp
vendored
Normal file
321
third-party/RakNet/src/RakNet/DS_Map.hpp
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
/// \file DS_Map.h
|
||||
/// \internal
|
||||
/// \brief Map
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __RAKNET_MAP_H
|
||||
#define __RAKNET_MAP_H
|
||||
|
||||
#include "DS_OrderedList.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
|
||||
// If I want to change this to a red-black tree, this is a good site: http://www.cs.auckland.ac.nz/software/AlgAnim/red_black.html
|
||||
// This makes insertions and deletions faster. But then traversals are slow, while they are currently fast.
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
/// The default comparison has to be first so it can be called as a default parameter.
|
||||
/// It then is followed by MapNode, followed by NodeComparisonFunc
|
||||
template <class key_type>
|
||||
int defaultMapKeyComparison(const key_type &a, const key_type &b)
|
||||
{
|
||||
if (a<b) return -1; if (a==b) return 0; return 1;
|
||||
}
|
||||
|
||||
/// \note IMPORTANT! If you use defaultMapKeyComparison then call IMPLEMENT_DEFAULT_COMPARISON or you will get an unresolved external linker error.
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&, const key_type&)=defaultMapKeyComparison<key_type> >
|
||||
class RAK_DLL_EXPORT Map
|
||||
{
|
||||
public:
|
||||
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<key_type>(key_type(),key_type());}
|
||||
|
||||
struct MapNode
|
||||
{
|
||||
MapNode() {}
|
||||
MapNode(key_type _key, data_type _data) : mapNodeKey(_key), mapNodeData(_data) {}
|
||||
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 <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
Map<key_type, data_type, key_comparison_func>::Map()
|
||||
{
|
||||
lastSearchIndexValid=false;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
Map<key_type, data_type, key_comparison_func>::~Map()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
Map<key_type, data_type, key_comparison_func>::Map( const Map& original_copy )
|
||||
{
|
||||
mapNodeList=original_copy.mapNodeList;
|
||||
lastSearchIndex=original_copy.lastSearchIndex;
|
||||
lastSearchKey=original_copy.lastSearchKey;
|
||||
lastSearchIndexValid=original_copy.lastSearchIndexValid;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
Map<key_type, data_type, key_comparison_func>& Map<key_type, data_type, key_comparison_func>::operator= ( const Map& original_copy )
|
||||
{
|
||||
mapNodeList=original_copy.mapNodeList;
|
||||
lastSearchIndex=original_copy.lastSearchIndex;
|
||||
lastSearchKey=original_copy.lastSearchKey;
|
||||
lastSearchIndexValid=original_copy.lastSearchIndexValid;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
data_type& Map<key_type, data_type, key_comparison_func>::Get(const key_type &key) 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 <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
unsigned Map<key_type, data_type, key_comparison_func>::GetIndexAtKey( const key_type &key )
|
||||
{
|
||||
if (HasSavedSearchResult(key))
|
||||
return lastSearchIndex;
|
||||
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
if (objectExists==false)
|
||||
{
|
||||
RakAssert(objectExists);
|
||||
}
|
||||
SaveLastSearch(key,index);
|
||||
return index;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::RemoveAtIndex(const unsigned index)
|
||||
{
|
||||
mapNodeList.RemoveAtIndex(index);
|
||||
lastSearchIndexValid=false;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
data_type Map<key_type, data_type, key_comparison_func>::Pop(const key_type &key)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
if (HasSavedSearchResult(key))
|
||||
index=lastSearchIndex;
|
||||
else
|
||||
{
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
RakAssert(objectExists);
|
||||
}
|
||||
data_type tmp = mapNodeList[index].mapNodeData;
|
||||
mapNodeList.RemoveAtIndex(index);
|
||||
lastSearchIndexValid=false;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::Set(const key_type &key, const data_type &data)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
|
||||
if (HasSavedSearchResult(key))
|
||||
{
|
||||
mapNodeList[lastSearchIndex].mapNodeData=data;
|
||||
return;
|
||||
}
|
||||
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
|
||||
if (objectExists)
|
||||
{
|
||||
SaveLastSearch(key,index);
|
||||
mapNodeList[index].mapNodeData=data;
|
||||
}
|
||||
else
|
||||
{
|
||||
SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_));
|
||||
}
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::SetExisting(const key_type &key, const data_type &data)
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
|
||||
if (HasSavedSearchResult(key))
|
||||
{
|
||||
index=lastSearchIndex;
|
||||
}
|
||||
else
|
||||
{
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
RakAssert(objectExists);
|
||||
SaveLastSearch(key,index);
|
||||
}
|
||||
|
||||
mapNodeList[index].mapNodeData=data;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::SetNew(const key_type &key, const data_type &data)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
bool objectExists;
|
||||
mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
RakAssert(objectExists==false);
|
||||
#endif
|
||||
SaveLastSearch(key,mapNodeList.Insert(key,MapNode(key,data), true, _FILE_AND_LINE_));
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
bool Map<key_type, data_type, key_comparison_func>::Has(const key_type &key) const
|
||||
{
|
||||
if (HasSavedSearchResult(key))
|
||||
return true;
|
||||
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
if (objectExists)
|
||||
SaveLastSearch(key,index);
|
||||
return objectExists;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
bool Map<key_type, data_type, key_comparison_func>::Delete(const key_type &key)
|
||||
{
|
||||
if (HasSavedSearchResult(key))
|
||||
{
|
||||
lastSearchIndexValid=false;
|
||||
mapNodeList.RemoveAtIndex(lastSearchIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index=mapNodeList.GetIndexFromKey(key, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
lastSearchIndexValid=false;
|
||||
mapNodeList.RemoveAtIndex(index);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::Clear(void)
|
||||
{
|
||||
lastSearchIndexValid=false;
|
||||
mapNodeList.Clear(false, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
data_type& Map<key_type, data_type, key_comparison_func>::operator[]( const unsigned int position ) const
|
||||
{
|
||||
return mapNodeList[position].mapNodeData;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
key_type Map<key_type, data_type, key_comparison_func>::GetKeyAtIndex( const unsigned int position ) const
|
||||
{
|
||||
return mapNodeList[position].mapNodeKey;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
unsigned Map<key_type, data_type, key_comparison_func>::Size(void) const
|
||||
{
|
||||
return mapNodeList.Size();
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
void Map<key_type, data_type, key_comparison_func>::SaveLastSearch(const key_type &key, const unsigned index) const
|
||||
{
|
||||
(void) key;
|
||||
(void) index;
|
||||
|
||||
/*
|
||||
lastSearchIndex=index;
|
||||
lastSearchKey=key;
|
||||
lastSearchIndexValid=true;
|
||||
*/
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*key_comparison_func)(const key_type&,const key_type&)>
|
||||
bool Map<key_type, data_type, key_comparison_func>::HasSavedSearchResult(const key_type &key) const
|
||||
{
|
||||
(void) key;
|
||||
|
||||
// Not threadsafe!
|
||||
return false;
|
||||
// return lastSearchIndexValid && key_comparison_func(key,lastSearchKey)==0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
294
third-party/RakNet/src/RakNet/DS_MemoryPool.hpp
vendored
Normal file
294
third-party/RakNet/src/RakNet/DS_MemoryPool.hpp
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
/// \file DS_MemoryPool.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __MEMORY_POOL_H
|
||||
#define __MEMORY_POOL_H
|
||||
|
||||
#ifndef __APPLE__
|
||||
// Use stdlib and not malloc for compatibility
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include "RakAssert.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
// DS_MEMORY_POOL_MAX_FREE_PAGES must be > 1
|
||||
#define DS_MEMORY_POOL_MAX_FREE_PAGES 4
|
||||
|
||||
//#define _DISABLE_MEMORY_POOL
|
||||
|
||||
namespace DataStructures
|
||||
{
|
||||
/// Very fast memory pool for allocating and deallocating structures that don't have constructors or destructors.
|
||||
/// Contains a list of pages, each of which has an array of the user structures
|
||||
template <class MemoryBlockType>
|
||||
class RAK_DLL_EXPORT MemoryPool
|
||||
{
|
||||
public:
|
||||
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 bytes
|
||||
MemoryBlockType *Allocate(const char *file, unsigned int line);
|
||||
void Release(MemoryBlockType *m, const char *file, unsigned int line);
|
||||
void Clear(const char *file, unsigned int line);
|
||||
|
||||
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, const char *file, unsigned int line);
|
||||
|
||||
// availablePages contains pages which have room to give the user new blocks. We return these blocks from the head of the list
|
||||
// unavailablePages are pages which are totally full, and from which we do not return new blocks.
|
||||
// Pages move from the head of unavailablePages to the tail of availablePages, and from the head of availablePages to the tail of unavailablePages
|
||||
Page *availablePages, *unavailablePages;
|
||||
int availablePagesSize, unavailablePagesSize;
|
||||
int memoryPoolPageSize;
|
||||
};
|
||||
|
||||
template<class MemoryBlockType>
|
||||
MemoryPool<MemoryBlockType>::MemoryPool()
|
||||
{
|
||||
#ifndef _DISABLE_MEMORY_POOL
|
||||
//AllocateFirst();
|
||||
availablePagesSize=0;
|
||||
unavailablePagesSize=0;
|
||||
memoryPoolPageSize=16384;
|
||||
#endif
|
||||
}
|
||||
template<class MemoryBlockType>
|
||||
MemoryPool<MemoryBlockType>::~MemoryPool()
|
||||
{
|
||||
#ifndef _DISABLE_MEMORY_POOL
|
||||
Clear(_FILE_AND_LINE_);
|
||||
#endif
|
||||
}
|
||||
|
||||
template<class MemoryBlockType>
|
||||
void MemoryPool<MemoryBlockType>::SetPageSize(int size)
|
||||
{
|
||||
memoryPoolPageSize=size;
|
||||
}
|
||||
|
||||
template<class MemoryBlockType>
|
||||
MemoryBlockType* MemoryPool<MemoryBlockType>::Allocate(const char *file, unsigned int line)
|
||||
{
|
||||
#ifdef _DISABLE_MEMORY_POOL
|
||||
return (MemoryBlockType*) rakMalloc_Ex(sizeof(MemoryBlockType), file, line);
|
||||
#else
|
||||
|
||||
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_Ex(sizeof(Page), file, line);
|
||||
if (availablePages==0)
|
||||
return 0;
|
||||
availablePagesSize=1;
|
||||
if (InitPage(availablePages, availablePages, file, line)==false)
|
||||
return 0;
|
||||
// If this assert hits, we couldn't allocate even 1 block per page. Increase the page size
|
||||
RakAssert(availablePages->availableStackSize>1);
|
||||
|
||||
return (MemoryBlockType *) availablePages->availableStack[--availablePages->availableStackSize];
|
||||
#endif
|
||||
}
|
||||
template<class MemoryBlockType>
|
||||
void MemoryPool<MemoryBlockType>::Release(MemoryBlockType *m, const char *file, unsigned int line)
|
||||
{
|
||||
#ifdef _DISABLE_MEMORY_POOL
|
||||
rakFree_Ex(m, file, line);
|
||||
return;
|
||||
#else
|
||||
// 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_Ex(curPage->availableStack, file, line );
|
||||
rakFree_Ex(curPage->block, file, line );
|
||||
rakFree_Ex(curPage, file, line );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
template<class MemoryBlockType>
|
||||
void MemoryPool<MemoryBlockType>::Clear(const char *file, unsigned int line)
|
||||
{
|
||||
#ifdef _DISABLE_MEMORY_POOL
|
||||
return;
|
||||
#else
|
||||
Page *cur, *freed;
|
||||
|
||||
if (availablePagesSize>0)
|
||||
{
|
||||
cur = availablePages;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#endif
|
||||
while (true)
|
||||
// do
|
||||
{
|
||||
rakFree_Ex(cur->availableStack, file, line );
|
||||
rakFree_Ex(cur->block, file, line );
|
||||
freed=cur;
|
||||
cur=cur->next;
|
||||
if (cur==availablePages)
|
||||
{
|
||||
rakFree_Ex(freed, file, line );
|
||||
break;
|
||||
}
|
||||
rakFree_Ex(freed, file, line );
|
||||
}// while(cur!=availablePages);
|
||||
}
|
||||
|
||||
if (unavailablePagesSize>0)
|
||||
{
|
||||
cur = unavailablePages;
|
||||
while (1)
|
||||
//do
|
||||
{
|
||||
rakFree_Ex(cur->availableStack, file, line );
|
||||
rakFree_Ex(cur->block, file, line );
|
||||
freed=cur;
|
||||
cur=cur->next;
|
||||
if (cur==unavailablePages)
|
||||
{
|
||||
rakFree_Ex(freed, file, line );
|
||||
break;
|
||||
}
|
||||
rakFree_Ex(freed, file, line );
|
||||
} // while(cur!=unavailablePages);
|
||||
}
|
||||
|
||||
availablePagesSize=0;
|
||||
unavailablePagesSize=0;
|
||||
#endif
|
||||
}
|
||||
template<class MemoryBlockType>
|
||||
int MemoryPool<MemoryBlockType>::BlocksPerPage(void) const
|
||||
{
|
||||
return memoryPoolPageSize / sizeof(MemoryWithPage);
|
||||
}
|
||||
template<class MemoryBlockType>
|
||||
bool MemoryPool<MemoryBlockType>::InitPage(Page *page, Page *prev, const char *file, unsigned int line)
|
||||
{
|
||||
int i=0;
|
||||
const int bpp = BlocksPerPage();
|
||||
page->block=(MemoryWithPage*) rakMalloc_Ex(memoryPoolPageSize, file, line);
|
||||
if (page->block==0)
|
||||
return false;
|
||||
page->availableStack=(MemoryWithPage**)rakMalloc_Ex(sizeof(MemoryWithPage*)*bpp, file, line);
|
||||
if (page->availableStack==0)
|
||||
{
|
||||
rakFree_Ex(page->block, file, line );
|
||||
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
|
||||
1652
third-party/RakNet/src/RakNet/DS_Multilist.hpp
vendored
Normal file
1652
third-party/RakNet/src/RakNet/DS_Multilist.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
244
third-party/RakNet/src/RakNet/DS_OrderedChannelHeap.hpp
vendored
Normal file
244
third-party/RakNet/src/RakNet/DS_OrderedChannelHeap.hpp
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
/// \file DS_OrderedChannelHeap.h
|
||||
/// \internal
|
||||
/// \brief 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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __RAKNET_ORDERED_CHANNEL_HEAP_H
|
||||
#define __RAKNET_ORDERED_CHANNEL_HEAP_H
|
||||
|
||||
#include "DS_Heap.hpp"
|
||||
#include "DS_Map.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "Rand.hpp"
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)=defaultMapKeyComparison<channel_key_type> >
|
||||
class RAK_DLL_EXPORT OrderedChannelHeap
|
||||
{
|
||||
public:
|
||||
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<channel_key_type>(channel_key_type(),channel_key_type());}
|
||||
|
||||
OrderedChannelHeap();
|
||||
~OrderedChannelHeap();
|
||||
void Push(const channel_key_type &channelID, const heap_data_type &data);
|
||||
void PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data);
|
||||
heap_data_type Pop(const unsigned startingIndex=0);
|
||||
heap_data_type Peek(const unsigned startingIndex) const;
|
||||
void AddChannel(const channel_key_type &channelID, const double weight);
|
||||
void RemoveChannel(channel_key_type channelID);
|
||||
void Clear(void);
|
||||
heap_data_type& operator[] ( const unsigned int position ) const;
|
||||
unsigned ChannelSize(const channel_key_type &channelID);
|
||||
unsigned Size(void) const;
|
||||
|
||||
struct QueueAndWeight
|
||||
{
|
||||
DataStructures::Queue<double> randResultQueue;
|
||||
double weight;
|
||||
bool signalDeletion;
|
||||
};
|
||||
|
||||
struct HeapChannelAndData
|
||||
{
|
||||
HeapChannelAndData() {}
|
||||
HeapChannelAndData(const channel_key_type &_channel, const heap_data_type &_data) : data(_data), channel(_channel) {}
|
||||
heap_data_type data;
|
||||
channel_key_type channel;
|
||||
};
|
||||
|
||||
protected:
|
||||
DataStructures::Map<channel_key_type, QueueAndWeight*, channel_key_comparison_func> map;
|
||||
DataStructures::Heap<double, HeapChannelAndData, true> heap;
|
||||
void GreatestRandResult(void);
|
||||
};
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::OrderedChannelHeap()
|
||||
{
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::~OrderedChannelHeap()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Push(const channel_key_type &channelID, const heap_data_type &data)
|
||||
{
|
||||
PushAtHead(MAX_UNSIGNED_LONG, channelID, data);
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::GreatestRandResult(void)
|
||||
{
|
||||
double greatest;
|
||||
unsigned i;
|
||||
greatest=0.0;
|
||||
for (i=0; i < map.Size(); i++)
|
||||
{
|
||||
if (map[i]->randResultQueue.Size() && map[i]->randResultQueue[0]>greatest)
|
||||
greatest=map[i]->randResultQueue[0];
|
||||
}
|
||||
return greatest;
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::PushAtHead(const unsigned index, const channel_key_type &channelID, const heap_data_type &data)
|
||||
{
|
||||
// If an assert hits here then this is an unknown channel. Call AddChannel first.
|
||||
QueueAndWeight *queueAndWeight=map.Get(channelID);
|
||||
double maxRange, minRange, rnd;
|
||||
if (queueAndWeight->randResultQueue.Size()==0)
|
||||
{
|
||||
// Set maxRange to the greatest random number waiting to be returned, rather than 1.0 necessarily
|
||||
// This is so weights are scaled similarly among channels. For example, if the head weight for a used channel was .25
|
||||
// and then we added another channel, the new channel would need to choose between .25 and 0
|
||||
// If we chose between 1.0 and 0, it would be 1/.25 (4x) more likely to be at the head of the heap than it should be
|
||||
maxRange=GreatestRandResult();
|
||||
if (maxRange==0.0)
|
||||
maxRange=1.0;
|
||||
minRange=0.0;
|
||||
}
|
||||
else if (index >= queueAndWeight->randResultQueue.Size())
|
||||
{
|
||||
maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999;
|
||||
minRange=0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (index==0)
|
||||
{
|
||||
maxRange=GreatestRandResult();
|
||||
if (maxRange==queueAndWeight->randResultQueue[0])
|
||||
maxRange=1.0;
|
||||
}
|
||||
else if (index >= queueAndWeight->randResultQueue.Size())
|
||||
maxRange=queueAndWeight->randResultQueue[queueAndWeight->randResultQueue.Size()-1]*.99999999;
|
||||
else
|
||||
maxRange=queueAndWeight->randResultQueue[index-1]*.99999999;
|
||||
|
||||
minRange=maxRange=queueAndWeight->randResultQueue[index]*1.00000001;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
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 <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
heap_data_type OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Pop(const unsigned startingIndex)
|
||||
{
|
||||
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 <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
heap_data_type OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Peek(const unsigned startingIndex) const
|
||||
{
|
||||
HeapChannelAndData heapChannelAndData = heap.Peek(startingIndex);
|
||||
return heapChannelAndData.data;
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::AddChannel(const channel_key_type &channelID, const double weight)
|
||||
{
|
||||
QueueAndWeight *qaw = RakNet::OP_NEW<QueueAndWeight>( _FILE_AND_LINE_ );
|
||||
qaw->weight=weight;
|
||||
qaw->signalDeletion=false;
|
||||
map.SetNew(channelID, qaw);
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::RemoveChannel(channel_key_type channelID)
|
||||
{
|
||||
if (map.Has(channelID))
|
||||
{
|
||||
unsigned i;
|
||||
i=map.GetIndexAtKey(channelID);
|
||||
if (map[i]->randResultQueue.Size()==0)
|
||||
{
|
||||
RakNet::OP_DELETE(map[i], _FILE_AND_LINE_);
|
||||
map.RemoveAtIndex(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Signal this channel for deletion later, because the heap has nodes with this channel right now
|
||||
map[i]->signalDeletion=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
unsigned OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Size(void) const
|
||||
{
|
||||
return heap.Size();
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
heap_data_type& OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::operator[]( const unsigned int position ) const
|
||||
{
|
||||
return heap[position].data;
|
||||
}
|
||||
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
unsigned OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::ChannelSize(const channel_key_type &channelID)
|
||||
{
|
||||
QueueAndWeight *queueAndWeight=map.Get(channelID);
|
||||
return queueAndWeight->randResultQueue.Size();
|
||||
}
|
||||
|
||||
template <class channel_key_type, class heap_data_type, int (*channel_key_comparison_func)(const channel_key_type&, const channel_key_type&)>
|
||||
void OrderedChannelHeap<channel_key_type, heap_data_type, channel_key_comparison_func>::Clear(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < map.Size(); i++)
|
||||
RakNet::OP_DELETE(map[i], _FILE_AND_LINE_);
|
||||
map.Clear(_FILE_AND_LINE_);
|
||||
heap.Clear(_FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
263
third-party/RakNet/src/RakNet/DS_OrderedList.hpp
vendored
Normal file
263
third-party/RakNet/src/RakNet/DS_OrderedList.hpp
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
/// \file DS_OrderedList.h
|
||||
/// \internal
|
||||
/// \brief Quicksort ordered list.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#include "DS_List.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
#ifndef __ORDERED_LIST_H
|
||||
#define __ORDERED_LIST_H
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class key_type, class data_type>
|
||||
int defaultOrderedListComparison(const key_type &a, const data_type &b)
|
||||
{
|
||||
if (a<b) return -1; if (a==b) return 0; return 1;
|
||||
}
|
||||
|
||||
/// \note IMPORTANT! If you use defaultOrderedListComparison then call IMPLEMENT_DEFAULT_COMPARISON or you will get an unresolved external linker error.
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)=defaultOrderedListComparison<key_type, data_type> >
|
||||
class RAK_DLL_EXPORT OrderedList
|
||||
{
|
||||
public:
|
||||
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultOrderedListComparison<key_type, data_type>(key_type(),data_type());}
|
||||
|
||||
OrderedList();
|
||||
~OrderedList();
|
||||
OrderedList( const OrderedList& original_copy );
|
||||
OrderedList& operator= ( const OrderedList& original_copy );
|
||||
|
||||
/// comparisonFunction must take a key_type and a data_type and return <0, ==0, or >0
|
||||
/// If the data type has comparison operators already defined then you can just use defaultComparison
|
||||
bool HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
|
||||
// GetIndexFromKey returns where the insert should go at the same time checks if it is there
|
||||
unsigned GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
|
||||
data_type GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
|
||||
bool GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)=default_comparison_function) const;
|
||||
unsigned Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, 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, const char *file, unsigned int line);
|
||||
void InsertAtEnd(const data_type &data, const char *file, unsigned int line);
|
||||
void RemoveFromEnd(const unsigned num=1);
|
||||
void Clear(bool doNotDeallocate, const char *file, unsigned int line);
|
||||
unsigned Size(void) const;
|
||||
|
||||
protected:
|
||||
DataStructures::List<data_type> orderedList;
|
||||
};
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
OrderedList<key_type, data_type, default_comparison_function>::OrderedList()
|
||||
{
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
OrderedList<key_type, data_type, default_comparison_function>::~OrderedList()
|
||||
{
|
||||
Clear(false, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
OrderedList<key_type, data_type, default_comparison_function>::OrderedList( const OrderedList& original_copy )
|
||||
{
|
||||
orderedList=original_copy.orderedList;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
OrderedList<key_type, data_type, default_comparison_function>& OrderedList<key_type, data_type, default_comparison_function>::operator= ( const OrderedList& original_copy )
|
||||
{
|
||||
orderedList=original_copy.orderedList;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
bool OrderedList<key_type, data_type, default_comparison_function>::HasData(const key_type &key, int (*cf)(const key_type&, const data_type&)) const
|
||||
{
|
||||
bool objectExists;
|
||||
GetIndexFromKey(key, &objectExists, cf);
|
||||
return objectExists;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
data_type OrderedList<key_type, data_type, default_comparison_function>::GetElementFromKey(const key_type &key, int (*cf)(const key_type&, const data_type&)) const
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index = GetIndexFromKey(key, &objectExists, cf);
|
||||
RakAssert(objectExists);
|
||||
return orderedList[index];
|
||||
}
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
bool OrderedList<key_type, data_type, default_comparison_function>::GetElementFromKey(const key_type &key, data_type &element, int (*cf)(const key_type&, const data_type&)) const
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index = GetIndexFromKey(key, &objectExists, cf);
|
||||
if (objectExists)
|
||||
element = orderedList[index];
|
||||
return objectExists;
|
||||
}
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
unsigned OrderedList<key_type, data_type, default_comparison_function>::GetIndexFromKey(const key_type &key, bool *objectExists, int (*cf)(const key_type&, const data_type&)) const
|
||||
{
|
||||
int index, upperBound, lowerBound;
|
||||
int res;
|
||||
|
||||
if (orderedList.Size()==0)
|
||||
{
|
||||
*objectExists=false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
upperBound=(int)orderedList.Size()-1;
|
||||
lowerBound=0;
|
||||
index = (int)orderedList.Size()/2;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
res = cf(key,orderedList[index]);
|
||||
if (res==0)
|
||||
{
|
||||
*objectExists=true;
|
||||
return index;
|
||||
}
|
||||
else if (res<0)
|
||||
{
|
||||
upperBound=index-1;
|
||||
}
|
||||
else// if (res>0)
|
||||
{
|
||||
lowerBound=index+1;
|
||||
}
|
||||
|
||||
index=lowerBound+(upperBound-lowerBound)/2;
|
||||
|
||||
if (lowerBound>upperBound)
|
||||
{
|
||||
*objectExists=false;
|
||||
return lowerBound; // No match
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
unsigned OrderedList<key_type, data_type, default_comparison_function>::Insert(const key_type &key, const data_type &data, bool assertOnDuplicate, const char *file, unsigned int line, 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!
|
||||
RakAssert(assertOnDuplicate==false);
|
||||
return (unsigned)-1;
|
||||
}
|
||||
|
||||
if (index>=orderedList.Size())
|
||||
{
|
||||
orderedList.Insert(data, file, line);
|
||||
return orderedList.Size()-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
orderedList.Insert(data,index, file, line);
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
unsigned OrderedList<key_type, data_type, default_comparison_function>::Remove(const key_type &key, int (*cf)(const key_type&, const data_type&))
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index = GetIndexFromKey(key, &objectExists, cf);
|
||||
|
||||
// Can't find the element to remove if this assert hits
|
||||
// RakAssert(objectExists==true);
|
||||
if (objectExists==false)
|
||||
{
|
||||
RakAssert(objectExists==true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
orderedList.RemoveAtIndex(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
unsigned OrderedList<key_type, data_type, default_comparison_function>::RemoveIfExists(const key_type &key, int (*cf)(const key_type&, const data_type&))
|
||||
{
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index = GetIndexFromKey(key, &objectExists, cf);
|
||||
|
||||
// Can't find the element to remove if this assert hits
|
||||
if (objectExists==false)
|
||||
return 0;
|
||||
|
||||
orderedList.RemoveAtIndex(index);
|
||||
return index;
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
void OrderedList<key_type, data_type, default_comparison_function>::RemoveAtIndex(const unsigned index)
|
||||
{
|
||||
orderedList.RemoveAtIndex(index);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
void OrderedList<key_type, data_type, default_comparison_function>::InsertAtIndex(const data_type &data, const unsigned index, const char *file, unsigned int line)
|
||||
{
|
||||
orderedList.Insert(data, index, file, line);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
void OrderedList<key_type, data_type, default_comparison_function>::InsertAtEnd(const data_type &data, const char *file, unsigned int line)
|
||||
{
|
||||
orderedList.Insert(data, file, line);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
void OrderedList<key_type, data_type, default_comparison_function>::RemoveFromEnd(const unsigned num)
|
||||
{
|
||||
orderedList.RemoveFromEnd(num);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
void OrderedList<key_type, data_type, default_comparison_function>::Clear(bool doNotDeallocate, const char *file, unsigned int line)
|
||||
{
|
||||
orderedList.Clear(doNotDeallocate, file, line);
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
data_type& OrderedList<key_type, data_type, default_comparison_function>::operator[]( const unsigned int position ) const
|
||||
{
|
||||
return orderedList[position];
|
||||
}
|
||||
|
||||
template <class key_type, class data_type, int (*default_comparison_function)(const key_type&, const data_type&)>
|
||||
unsigned OrderedList<key_type, data_type, default_comparison_function>::Size(void) const
|
||||
{
|
||||
return orderedList.Size();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
435
third-party/RakNet/src/RakNet/DS_Queue.hpp
vendored
Normal file
435
third-party/RakNet/src/RakNet/DS_Queue.hpp
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
/// \file DS_Queue.h
|
||||
/// \internal
|
||||
/// \brief A queue used by RakNet.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __QUEUE_H
|
||||
#define __QUEUE_H
|
||||
|
||||
// Template classes have to have all the code in the header file
|
||||
#include "RakAssert.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
/// \brief A queue implemented as an array with a read and write index.
|
||||
template <class queue_type>
|
||||
class RAK_DLL_EXPORT Queue
|
||||
{
|
||||
public:
|
||||
Queue();
|
||||
~Queue();
|
||||
Queue( Queue& original_copy );
|
||||
bool operator= ( const Queue& original_copy );
|
||||
void Push( const queue_type& input, const char *file, unsigned int line );
|
||||
void PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line );
|
||||
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 );
|
||||
// Debug: Set pointer to 0, for memory leak detection
|
||||
inline queue_type PopDeref( void );
|
||||
inline unsigned int Size( void ) const;
|
||||
inline bool IsEmpty(void) const;
|
||||
inline unsigned int AllocationSize( void ) const;
|
||||
inline void Clear( const char *file, unsigned int line );
|
||||
void Compress( const char *file, unsigned int line );
|
||||
bool Find ( queue_type q );
|
||||
void ClearAndForceAllocation( int size, const char *file, unsigned int line ); // Force a memory allocation to a certain larger size
|
||||
|
||||
private:
|
||||
queue_type* array;
|
||||
unsigned int head; // Array index for the head of the queue
|
||||
unsigned int tail; // Array index for the tail of the queue
|
||||
unsigned int allocation_size;
|
||||
};
|
||||
|
||||
|
||||
template <class queue_type>
|
||||
inline unsigned int Queue<queue_type>::Size( void ) const
|
||||
{
|
||||
if ( head <= tail )
|
||||
return tail -head;
|
||||
else
|
||||
return allocation_size -head + tail;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline bool Queue<queue_type>::IsEmpty(void) const
|
||||
{
|
||||
return head==tail;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline unsigned int Queue<queue_type>::AllocationSize( void ) const
|
||||
{
|
||||
return allocation_size;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
Queue<queue_type>::Queue()
|
||||
{
|
||||
//allocation_size = 16;
|
||||
//array = RakNet::OP_NEW_ARRAY<queue_type>(allocation_size, _FILE_AND_LINE_ );
|
||||
allocation_size = 0;
|
||||
array=0;
|
||||
head = 0;
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
Queue<queue_type>::~Queue()
|
||||
{
|
||||
if (allocation_size>0)
|
||||
RakNet::OP_DELETE_ARRAY(array, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline queue_type Queue<queue_type>::Pop( void )
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert( head != tail);
|
||||
#endif
|
||||
//head=(head+1) % allocation_size;
|
||||
|
||||
if ( ++head == allocation_size )
|
||||
head = 0;
|
||||
|
||||
if ( head == 0 )
|
||||
return ( queue_type ) array[ allocation_size -1 ];
|
||||
|
||||
return ( queue_type ) array[ head -1 ];
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline queue_type Queue<queue_type>::PopDeref( void )
|
||||
{
|
||||
if ( ++head == allocation_size )
|
||||
head = 0;
|
||||
|
||||
queue_type q;
|
||||
if ( head == 0 )
|
||||
{
|
||||
q=array[ allocation_size -1 ];
|
||||
array[ allocation_size -1 ]=0;
|
||||
return q;
|
||||
}
|
||||
|
||||
q=array[ head -1 ];
|
||||
array[ head -1 ]=0;
|
||||
return q;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
void Queue<queue_type>::PushAtHead( const queue_type& input, unsigned index, const char *file, unsigned int line )
|
||||
{
|
||||
RakAssert(index <= Size());
|
||||
|
||||
// Just force a reallocation, will be overwritten
|
||||
Push(input, file, line );
|
||||
|
||||
if (Size()==1)
|
||||
return;
|
||||
|
||||
unsigned writeIndex, readIndex, trueWriteIndex, trueReadIndex;
|
||||
writeIndex=Size()-1;
|
||||
readIndex=writeIndex-1;
|
||||
while (readIndex >= index)
|
||||
{
|
||||
if ( head + writeIndex >= allocation_size )
|
||||
trueWriteIndex = head + writeIndex - allocation_size;
|
||||
else
|
||||
trueWriteIndex = head + writeIndex;
|
||||
|
||||
if ( head + readIndex >= allocation_size )
|
||||
trueReadIndex = head + readIndex - allocation_size;
|
||||
else
|
||||
trueReadIndex = head + readIndex;
|
||||
|
||||
array[trueWriteIndex]=array[trueReadIndex];
|
||||
|
||||
if (readIndex==0)
|
||||
break;
|
||||
writeIndex--;
|
||||
readIndex--;
|
||||
}
|
||||
|
||||
if ( head + index >= allocation_size )
|
||||
trueWriteIndex = head + index - allocation_size;
|
||||
else
|
||||
trueWriteIndex = head + index;
|
||||
|
||||
array[trueWriteIndex]=input;
|
||||
}
|
||||
|
||||
|
||||
template <class queue_type>
|
||||
inline queue_type Queue<queue_type>::Peek( void ) const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert( head != tail );
|
||||
#endif
|
||||
|
||||
return ( queue_type ) array[ head ];
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline queue_type Queue<queue_type>::PeekTail( void ) const
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert( head != tail );
|
||||
#endif
|
||||
if (tail!=0)
|
||||
return ( queue_type ) array[ tail-1 ];
|
||||
else
|
||||
return ( queue_type ) array[ allocation_size-1 ];
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
void Queue<queue_type>::Push( const queue_type& input, const char *file, unsigned int line )
|
||||
{
|
||||
if ( allocation_size == 0 )
|
||||
{
|
||||
array = RakNet::OP_NEW_ARRAY<queue_type>(16, file, line );
|
||||
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<queue_type>(allocation_size * 2, file, line );
|
||||
#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, file, line);
|
||||
|
||||
array = new_array;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
Queue<queue_type>::Queue( Queue& original_copy )
|
||||
{
|
||||
// Allocate memory for copy
|
||||
|
||||
if ( original_copy.Size() == 0 )
|
||||
{
|
||||
allocation_size = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
array = RakNet::OP_NEW_ARRAY<queue_type >( original_copy.Size() + 1 , _FILE_AND_LINE_ );
|
||||
|
||||
for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter )
|
||||
array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ];
|
||||
|
||||
head = 0;
|
||||
|
||||
tail = original_copy.Size();
|
||||
|
||||
allocation_size = original_copy.Size() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
bool Queue<queue_type>::operator= ( const Queue& original_copy )
|
||||
{
|
||||
if ( ( &original_copy ) == this )
|
||||
return false;
|
||||
|
||||
Clear(_FILE_AND_LINE_);
|
||||
|
||||
// Allocate memory for copy
|
||||
if ( original_copy.Size() == 0 )
|
||||
{
|
||||
allocation_size = 0;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
array = RakNet::OP_NEW_ARRAY<queue_type >( original_copy.Size() + 1 , _FILE_AND_LINE_ );
|
||||
|
||||
for ( unsigned int counter = 0; counter < original_copy.Size(); ++counter )
|
||||
array[ counter ] = original_copy.array[ ( original_copy.head + counter ) % ( original_copy.allocation_size ) ];
|
||||
|
||||
head = 0;
|
||||
|
||||
tail = original_copy.Size();
|
||||
|
||||
allocation_size = original_copy.Size() + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline void Queue<queue_type>::Clear ( const char *file, unsigned int line )
|
||||
{
|
||||
if ( allocation_size == 0 )
|
||||
return ;
|
||||
|
||||
if (allocation_size > 32)
|
||||
{
|
||||
RakNet::OP_DELETE_ARRAY(array, file, line);
|
||||
allocation_size = 0;
|
||||
}
|
||||
|
||||
head = 0;
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
void Queue<queue_type>::Compress ( const char *file, unsigned int line )
|
||||
{
|
||||
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<queue_type >(newAllocationSize, file, line );
|
||||
|
||||
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, file, line);
|
||||
array=new_array;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
bool Queue<queue_type>::Find ( queue_type q )
|
||||
{
|
||||
if ( allocation_size == 0 )
|
||||
return false;
|
||||
|
||||
unsigned int counter = head;
|
||||
|
||||
while ( counter != tail )
|
||||
{
|
||||
if ( array[ counter ] == q )
|
||||
return true;
|
||||
|
||||
counter = ( counter + 1 ) % allocation_size;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
void Queue<queue_type>::ClearAndForceAllocation( int size, const char *file, unsigned int line )
|
||||
{
|
||||
RakNet::OP_DELETE_ARRAY(array, file, line);
|
||||
if (size>0)
|
||||
array = RakNet::OP_NEW_ARRAY<queue_type>(size, file, line );
|
||||
else
|
||||
array=0;
|
||||
allocation_size = size;
|
||||
head = 0;
|
||||
tail = 0;
|
||||
}
|
||||
|
||||
template <class queue_type>
|
||||
inline queue_type& Queue<queue_type>::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 <class queue_type>
|
||||
void Queue<queue_type>::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
|
||||
|
||||
103
third-party/RakNet/src/RakNet/DS_QueueLinkedList.hpp
vendored
Normal file
103
third-party/RakNet/src/RakNet/DS_QueueLinkedList.hpp
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
/// \file DS_QueueLinkedList.h
|
||||
/// \internal
|
||||
/// \brief A queue implemented as a linked list.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __QUEUE_LINKED_LIST_H
|
||||
#define __QUEUE_LINKED_LIST_H
|
||||
|
||||
#include "DS_LinkedList.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
/// \brief A queue implemented using a linked list. Rarely used.
|
||||
template <class QueueType>
|
||||
class RAK_DLL_EXPORT QueueLinkedList
|
||||
{
|
||||
|
||||
public:
|
||||
QueueLinkedList();
|
||||
QueueLinkedList( const QueueLinkedList& original_copy );
|
||||
bool operator= ( const QueueLinkedList& original_copy );
|
||||
QueueType Pop( void );
|
||||
QueueType& Peek( void );
|
||||
QueueType& EndPeek( void );
|
||||
void Push( const QueueType& input );
|
||||
unsigned int Size( void );
|
||||
void Clear( void );
|
||||
void Compress( void );
|
||||
|
||||
private:
|
||||
LinkedList<QueueType> data;
|
||||
};
|
||||
|
||||
template <class QueueType>
|
||||
QueueLinkedList<QueueType>::QueueLinkedList()
|
||||
{
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
inline unsigned int QueueLinkedList<QueueType>::Size()
|
||||
{
|
||||
return data.Size();
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
inline QueueType QueueLinkedList<QueueType>::Pop( void )
|
||||
{
|
||||
data.Beginning();
|
||||
return ( QueueType ) data.Pop();
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
inline QueueType& QueueLinkedList<QueueType>::Peek( void )
|
||||
{
|
||||
data.Beginning();
|
||||
return ( QueueType ) data.Peek();
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
inline QueueType& QueueLinkedList<QueueType>::EndPeek( void )
|
||||
{
|
||||
data.End();
|
||||
return ( QueueType ) data.Peek();
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
void QueueLinkedList<QueueType>::Push( const QueueType& input )
|
||||
{
|
||||
data.End();
|
||||
data.Add( input );
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
QueueLinkedList<QueueType>::QueueLinkedList( const QueueLinkedList& original_copy )
|
||||
{
|
||||
data = original_copy.data;
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
bool QueueLinkedList<QueueType>::operator= ( const QueueLinkedList& original_copy )
|
||||
{
|
||||
if ( ( &original_copy ) == this )
|
||||
return false;
|
||||
|
||||
data = original_copy.data;
|
||||
}
|
||||
|
||||
template <class QueueType>
|
||||
void QueueLinkedList<QueueType>::Clear ( void )
|
||||
{
|
||||
data.Clear();
|
||||
}
|
||||
} // End namespace
|
||||
|
||||
#endif
|
||||
236
third-party/RakNet/src/RakNet/DS_RangeList.hpp
vendored
Normal file
236
third-party/RakNet/src/RakNet/DS_RangeList.hpp
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
/// \file DS_RangeList.h
|
||||
/// \internal
|
||||
/// \brief A queue implemented as a linked list.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __RANGE_LIST_H
|
||||
#define __RANGE_LIST_H
|
||||
|
||||
#include "DS_OrderedList.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class range_type>
|
||||
struct RangeNode
|
||||
{
|
||||
RangeNode() {}
|
||||
~RangeNode() {}
|
||||
RangeNode(range_type min, range_type max) {minIndex=min; maxIndex=max;}
|
||||
range_type minIndex;
|
||||
range_type maxIndex;
|
||||
};
|
||||
|
||||
|
||||
template <class range_type>
|
||||
int RangeNodeComp(const range_type &a, const RangeNode<range_type> &b)
|
||||
{
|
||||
if (a<b.minIndex)
|
||||
return -1;
|
||||
if (a==b.minIndex)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
class RAK_DLL_EXPORT RangeList
|
||||
{
|
||||
public:
|
||||
RangeList();
|
||||
~RangeList();
|
||||
void Insert(range_type index);
|
||||
void Clear(void);
|
||||
unsigned Size(void) const;
|
||||
unsigned RangeSum(void) const;
|
||||
RakNet::BitSize_t Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized);
|
||||
bool Deserialize(RakNet::BitStream *out);
|
||||
|
||||
DataStructures::OrderedList<range_type, RangeNode<range_type> , RangeNodeComp<range_type> > ranges;
|
||||
};
|
||||
|
||||
template <class range_type>
|
||||
RakNet::BitSize_t RangeList<range_type>::Serialize(RakNet::BitStream *in, RakNet::BitSize_t maxBits, bool clearSerialized)
|
||||
{
|
||||
RakAssert(ranges.Size() < (unsigned short)-1);
|
||||
RakNet::BitStream tempBS;
|
||||
RakNet::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;
|
||||
unsigned char minEqualsMax;
|
||||
if (ranges[i].minIndex==ranges[i].maxIndex)
|
||||
minEqualsMax=1;
|
||||
else
|
||||
minEqualsMax=0;
|
||||
tempBS.Write(minEqualsMax); // Use one byte, intead of one bit, for speed, as this is done a lot
|
||||
tempBS.Write(ranges[i].minIndex);
|
||||
bitsWritten+=sizeof(range_type)*8+8;
|
||||
if (ranges[i].minIndex!=ranges[i].maxIndex)
|
||||
{
|
||||
tempBS.Write(ranges[i].maxIndex);
|
||||
bitsWritten+=sizeof(range_type)*8;
|
||||
}
|
||||
countWritten++;
|
||||
}
|
||||
|
||||
in->AlignWriteToByteBoundary();
|
||||
RakNet::BitSize_t before=in->GetWriteOffset();
|
||||
in->Write(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 <class range_type>
|
||||
bool RangeList<range_type>::Deserialize(RakNet::BitStream *out)
|
||||
{
|
||||
ranges.Clear(true, _FILE_AND_LINE_);
|
||||
unsigned short count;
|
||||
out->AlignReadToByteBoundary();
|
||||
out->Read(count);
|
||||
unsigned short i;
|
||||
range_type min,max;
|
||||
unsigned char maxEqualToMin=0;
|
||||
|
||||
for (i=0; i < count; i++)
|
||||
{
|
||||
out->Read(maxEqualToMin);
|
||||
if (out->Read(min)==false)
|
||||
return false;
|
||||
if (maxEqualToMin==false)
|
||||
{
|
||||
if (out->Read(max)==false)
|
||||
return false;
|
||||
if (max<min)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
max=min;
|
||||
|
||||
|
||||
ranges.InsertAtEnd(RangeNode<range_type>(min,max), _FILE_AND_LINE_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
RangeList<range_type>::RangeList()
|
||||
{
|
||||
RangeNodeComp<range_type>(0, RangeNode<range_type>());
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
RangeList<range_type>::~RangeList()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
void RangeList<range_type>::Insert(range_type index)
|
||||
{
|
||||
if (ranges.Size()==0)
|
||||
{
|
||||
ranges.Insert(index, RangeNode<range_type>(index, index), true, _FILE_AND_LINE_);
|
||||
return;
|
||||
}
|
||||
|
||||
bool objectExists;
|
||||
unsigned insertionIndex=ranges.GetIndexFromKey(index, &objectExists);
|
||||
if (insertionIndex==ranges.Size())
|
||||
{
|
||||
if (index == ranges[insertionIndex-1].maxIndex+(range_type)1)
|
||||
ranges[insertionIndex-1].maxIndex++;
|
||||
else if (index > ranges[insertionIndex-1].maxIndex+(range_type)1)
|
||||
{
|
||||
// Insert at end
|
||||
ranges.Insert(index, RangeNode<range_type>(index, index), true, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < ranges[insertionIndex].minIndex-(range_type)1)
|
||||
{
|
||||
// Insert here
|
||||
ranges.InsertAtIndex(RangeNode<range_type>(index, index), insertionIndex, _FILE_AND_LINE_);
|
||||
|
||||
return;
|
||||
}
|
||||
else if (index == ranges[insertionIndex].minIndex-(range_type)1)
|
||||
{
|
||||
// Decrease minIndex and join left
|
||||
ranges[insertionIndex].minIndex--;
|
||||
if (insertionIndex>0 && ranges[insertionIndex-1].maxIndex+(range_type)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+(range_type)1)
|
||||
{
|
||||
// Increase maxIndex and join right
|
||||
ranges[insertionIndex].maxIndex++;
|
||||
if (insertionIndex<ranges.Size()-1 && ranges[insertionIndex+(range_type)1].minIndex==ranges[insertionIndex].maxIndex+(range_type)1)
|
||||
{
|
||||
ranges[insertionIndex+1].minIndex=ranges[insertionIndex].minIndex;
|
||||
ranges.RemoveAtIndex(insertionIndex);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
void RangeList<range_type>::Clear(void)
|
||||
{
|
||||
ranges.Clear(true, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
unsigned RangeList<range_type>::Size(void) const
|
||||
{
|
||||
return ranges.Size();
|
||||
}
|
||||
|
||||
template <class range_type>
|
||||
unsigned RangeList<range_type>::RangeSum(void) const
|
||||
{
|
||||
unsigned sum=0,i;
|
||||
for (i=0; i < ranges.Size(); i++)
|
||||
sum+=ranges[i].maxIndex-ranges[i].minIndex+1;
|
||||
return sum;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
1128
third-party/RakNet/src/RakNet/DS_Table.cpp
vendored
Normal file
1128
third-party/RakNet/src/RakNet/DS_Table.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
343
third-party/RakNet/src/RakNet/DS_Table.hpp
vendored
Normal file
343
third-party/RakNet/src/RakNet/DS_Table.hpp
vendored
Normal file
@@ -0,0 +1,343 @@
|
||||
/// \file DS_Table.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __TABLE_H
|
||||
#define __TABLE_H
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
#include "DS_List.hpp"
|
||||
#include "DS_BPlusTree.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "RakString.hpp"
|
||||
|
||||
#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.
|
||||
/// \details The table data structure is useful if you want to store a set of structures and perform queries on those structures.<BR>
|
||||
/// This is a relatively simple and fast implementation of the types of tables commonly used in databases.<BR>
|
||||
/// See TableSerializer to serialize data members of the table.<BR>
|
||||
/// 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
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
struct RAK_DLL_EXPORT Cell
|
||||
{
|
||||
Cell();
|
||||
~Cell();
|
||||
Cell(double numericValue, char *charValue, void *ptr, ColumnType type);
|
||||
void SetByType(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
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
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
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
struct RAK_DLL_EXPORT Row
|
||||
{
|
||||
// list of cells
|
||||
DataStructures::List<Cell*> 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.
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
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
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
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
|
||||
/// \details 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]) const;
|
||||
unsigned ColumnIndex(const char *columnName) const;
|
||||
|
||||
/// \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
|
||||
/// \details New rows are added with empty values for all cells. However, if you specify initialCelLValues you can specify initial values
|
||||
/// It's up to you to ensure that the values in the specific cells match the type of data used by that row
|
||||
/// rowId can be considered the primary key for the row. It is much faster to lookup a row by its rowId than by searching keys.
|
||||
/// rowId must be unique
|
||||
/// Rows are stored in sorted order in the table, using rowId as the sort key
|
||||
/// \param[in] rowId The UNIQUE primary key for the row. This can never be changed.
|
||||
/// \param[in] initialCellValues Initial values to give the row (optional)
|
||||
/// \return The newly added row
|
||||
Table::Row* AddRow(unsigned rowId);
|
||||
Table::Row* AddRow(unsigned rowId, DataStructures::List<Cell> &initialCellValues);
|
||||
Table::Row* AddRow(unsigned rowId, DataStructures::List<Cell*> &initialCellValues, bool copyCells=false);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief Removes all the rows with IDs that the specified table also has.
|
||||
/// \param[in] tableContainingRowIDs The IDs of the rows
|
||||
void RemoveRows(Table *tableContainingRowIDs);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief 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
|
||||
/// \details 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);
|
||||
|
||||
/// \brief Frees all memory in the table.
|
||||
void Clear(void);
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief 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;
|
||||
|
||||
/// \brief Direct access to make things easier.
|
||||
const DataStructures::List<ColumnDescriptor>& GetColumns(void) const;
|
||||
|
||||
/// \brief Direct access to make things easier.
|
||||
const DataStructures::BPlusTree<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER>& GetRows(void) const;
|
||||
|
||||
/// \brief Get the head of a linked list containing all the row data.
|
||||
DataStructures::Page<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> * GetListHead(void);
|
||||
|
||||
/// \brief 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<unsigned> columnIndices);
|
||||
|
||||
void DeleteRow(Row *row);
|
||||
|
||||
void QueryRow(DataStructures::List<unsigned> &inclusionFilterColumnIndices, DataStructures::List<unsigned> &columnIndicesToReturn, unsigned key, Table::Row* row, FilterQuery *inclusionFilters, Table *result);
|
||||
|
||||
// 16 is arbitrary and is the order of the BPlus tree. Higher orders are better for searching while lower orders are better for
|
||||
// Insertions and deletions.
|
||||
DataStructures::BPlusTree<unsigned, Row*, _TABLE_BPLUS_TREE_ORDER> rows;
|
||||
|
||||
// Columns in the table.
|
||||
DataStructures::List<ColumnDescriptor> columns;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
143
third-party/RakNet/src/RakNet/DS_ThreadsafeAllocatingQueue.hpp
vendored
Normal file
143
third-party/RakNet/src/RakNet/DS_ThreadsafeAllocatingQueue.hpp
vendored
Normal file
@@ -0,0 +1,143 @@
|
||||
/// \file DS_ThreadsafeAllocatingQueue.h
|
||||
/// \internal
|
||||
/// A threadsafe queue, that also uses a memory pool for allocation
|
||||
|
||||
#ifndef __THREADSAFE_ALLOCATING_QUEUE
|
||||
#define __THREADSAFE_ALLOCATING_QUEUE
|
||||
|
||||
#include "DS_Queue.hpp"
|
||||
#include "SimpleMutex.hpp"
|
||||
#include "DS_MemoryPool.hpp"
|
||||
|
||||
// #if defined(new)
|
||||
// #pragma push_macro("new")
|
||||
// #undef new
|
||||
// #define RMO_NEW_UNDEF_ALLOCATING_QUEUE
|
||||
// #endif
|
||||
|
||||
namespace DataStructures
|
||||
{
|
||||
|
||||
template <class structureType>
|
||||
class RAK_DLL_EXPORT ThreadsafeAllocatingQueue
|
||||
{
|
||||
public:
|
||||
// Queue operations
|
||||
void Push(structureType *s);
|
||||
structureType *PopInaccurate(void);
|
||||
structureType *Pop(void);
|
||||
void SetPageSize(int size);
|
||||
bool IsEmpty(void);
|
||||
|
||||
// Memory pool operations
|
||||
structureType *Allocate(const char *file, unsigned int line);
|
||||
void Deallocate(structureType *s, const char *file, unsigned int line);
|
||||
void Clear(const char *file, unsigned int line);
|
||||
protected:
|
||||
|
||||
MemoryPool<structureType> memoryPool;
|
||||
RakNet::SimpleMutex memoryPoolMutex;
|
||||
Queue<structureType*> queue;
|
||||
RakNet::SimpleMutex queueMutex;
|
||||
};
|
||||
|
||||
template <class structureType>
|
||||
void ThreadsafeAllocatingQueue<structureType>::Push(structureType *s)
|
||||
{
|
||||
queueMutex.Lock();
|
||||
queue.Push(s, _FILE_AND_LINE_ );
|
||||
queueMutex.Unlock();
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
structureType *ThreadsafeAllocatingQueue<structureType>::PopInaccurate(void)
|
||||
{
|
||||
structureType *s;
|
||||
if (queue.IsEmpty())
|
||||
return 0;
|
||||
queueMutex.Lock();
|
||||
if (queue.IsEmpty()==false)
|
||||
s=queue.Pop();
|
||||
else
|
||||
s=0;
|
||||
queueMutex.Unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
structureType *ThreadsafeAllocatingQueue<structureType>::Pop(void)
|
||||
{
|
||||
structureType *s;
|
||||
queueMutex.Lock();
|
||||
if (queue.IsEmpty())
|
||||
{
|
||||
queueMutex.Unlock();
|
||||
return 0;
|
||||
}
|
||||
s=queue.Pop();
|
||||
queueMutex.Unlock();
|
||||
return s;
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
structureType *ThreadsafeAllocatingQueue<structureType>::Allocate(const char *file, unsigned int line)
|
||||
{
|
||||
structureType *s;
|
||||
memoryPoolMutex.Lock();
|
||||
s=memoryPool.Allocate(file, line);
|
||||
memoryPoolMutex.Unlock();
|
||||
// Call new operator, memoryPool doesn't do this
|
||||
s = new ((void*)s) structureType;
|
||||
return s;
|
||||
}
|
||||
template <class structureType>
|
||||
void ThreadsafeAllocatingQueue<structureType>::Deallocate(structureType *s, const char *file, unsigned int line)
|
||||
{
|
||||
// Call delete operator, memory pool doesn't do this
|
||||
s->~structureType();
|
||||
memoryPoolMutex.Lock();
|
||||
memoryPool.Release(s, file, line);
|
||||
memoryPoolMutex.Unlock();
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
void ThreadsafeAllocatingQueue<structureType>::Clear(const char *file, unsigned int line)
|
||||
{
|
||||
memoryPoolMutex.Lock();
|
||||
for (unsigned int i=0; i < queue.Size(); i++)
|
||||
{
|
||||
queue[i]->~structureType();
|
||||
memoryPool.Release(queue[i], file, line);
|
||||
}
|
||||
queue.Clear(file, line);
|
||||
memoryPoolMutex.Unlock();
|
||||
memoryPoolMutex.Lock();
|
||||
memoryPool.Clear(file, line);
|
||||
memoryPoolMutex.Unlock();
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
void ThreadsafeAllocatingQueue<structureType>::SetPageSize(int size)
|
||||
{
|
||||
memoryPool.SetPageSize(size);
|
||||
}
|
||||
|
||||
template <class structureType>
|
||||
bool ThreadsafeAllocatingQueue<structureType>::IsEmpty(void)
|
||||
{
|
||||
bool isEmpty;
|
||||
queueMutex.Lock();
|
||||
isEmpty=queue.IsEmpty();
|
||||
queueMutex.Unlock();
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
// #if defined(RMO_NEW_UNDEF_ALLOCATING_QUEUE)
|
||||
// #pragma pop_macro("new")
|
||||
// #undef RMO_NEW_UNDEF_ALLOCATING_QUEUE
|
||||
// #endif
|
||||
|
||||
#endif
|
||||
98
third-party/RakNet/src/RakNet/DS_Tree.hpp
vendored
Normal file
98
third-party/RakNet/src/RakNet/DS_Tree.hpp
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
/// \file DS_Tree.h
|
||||
/// \internal
|
||||
/// \brief Just a regular tree
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __DS_TREE_H
|
||||
#define __DS_TREE_H
|
||||
|
||||
#include "Export.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class TreeType>
|
||||
class RAK_DLL_EXPORT Tree
|
||||
{
|
||||
public:
|
||||
Tree();
|
||||
Tree(TreeType &inputData);
|
||||
~Tree();
|
||||
void LevelOrderTraversal(DataStructures::List<Tree*> &output);
|
||||
void AddChild(TreeType &newData);
|
||||
void DeleteDecendants(void);
|
||||
|
||||
TreeType data;
|
||||
DataStructures::List<Tree *> children;
|
||||
};
|
||||
|
||||
template <class TreeType>
|
||||
Tree<TreeType>::Tree()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
template <class TreeType>
|
||||
Tree<TreeType>::Tree(TreeType &inputData)
|
||||
{
|
||||
data=inputData;
|
||||
}
|
||||
|
||||
template <class TreeType>
|
||||
Tree<TreeType>::~Tree()
|
||||
{
|
||||
DeleteDecendants();
|
||||
}
|
||||
|
||||
template <class TreeType>
|
||||
void Tree<TreeType>::LevelOrderTraversal(DataStructures::List<Tree*> &output)
|
||||
{
|
||||
unsigned i;
|
||||
Tree<TreeType> *node;
|
||||
DataStructures::Queue<Tree<TreeType>*> queue;
|
||||
|
||||
for (i=0; i < children.Size(); i++)
|
||||
queue.Push(children[i]);
|
||||
|
||||
while (queue.Size())
|
||||
{
|
||||
node=queue.Pop();
|
||||
output.Insert(node, _FILE_AND_LINE_);
|
||||
for (i=0; i < node->children.Size(); i++)
|
||||
queue.Push(node->children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
template <class TreeType>
|
||||
void Tree<TreeType>::AddChild(TreeType &newData)
|
||||
{
|
||||
children.Insert(RakNet::OP_NEW<Tree>(newData, _FILE_AND_LINE_));
|
||||
}
|
||||
|
||||
template <class TreeType>
|
||||
void Tree<TreeType>::DeleteDecendants(void)
|
||||
{
|
||||
/*
|
||||
DataStructures::List<Tree*> output;
|
||||
LevelOrderTraversal(output);
|
||||
unsigned i;
|
||||
for (i=0; i < output.Size(); i++)
|
||||
RakNet::OP_DELETE(output[i], _FILE_AND_LINE_);
|
||||
*/
|
||||
|
||||
// Already recursive to do this
|
||||
unsigned int i;
|
||||
for (i=0; i < children.Size(); i++)
|
||||
RakNet::OP_DELETE(children[i], _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
537
third-party/RakNet/src/RakNet/DS_WeightedGraph.hpp
vendored
Normal file
537
third-party/RakNet/src/RakNet/DS_WeightedGraph.hpp
vendored
Normal file
@@ -0,0 +1,537 @@
|
||||
/// \file DS_WeightedGraph.h
|
||||
/// \internal
|
||||
/// \brief Weighted graph.
|
||||
/// \details 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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __WEIGHTED_GRAPH_H
|
||||
#define __WEIGHTED_GRAPH_H
|
||||
|
||||
#include "DS_OrderedList.hpp"
|
||||
#include "DS_Map.hpp"
|
||||
#include "DS_Heap.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "DS_Tree.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#ifdef _DEBUG
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
/// The namespace DataStructures was only added to avoid compiler errors for commonly named data structures
|
||||
/// As these data structures are stand-alone, you can use them outside of RakNet for your own projects if you wish.
|
||||
namespace DataStructures
|
||||
{
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
class RAK_DLL_EXPORT WeightedGraph
|
||||
{
|
||||
public:
|
||||
static void IMPLEMENT_DEFAULT_COMPARISON(void) {DataStructures::defaultMapKeyComparison<node_type>(node_type(),node_type());}
|
||||
|
||||
WeightedGraph();
|
||||
~WeightedGraph();
|
||||
WeightedGraph( const WeightedGraph& original_copy );
|
||||
WeightedGraph& operator= ( const WeightedGraph& original_copy );
|
||||
void AddNode(const node_type &node);
|
||||
void RemoveNode(const node_type &node);
|
||||
void AddConnection(const node_type &node1, const node_type &node2, weight_type weight);
|
||||
void RemoveConnection(const node_type &node1, const node_type &node2);
|
||||
bool HasConnection(const node_type &node1, const node_type &node2);
|
||||
void Print(void);
|
||||
void Clear(void);
|
||||
bool GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT);
|
||||
bool GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT );
|
||||
unsigned GetNodeCount(void) const;
|
||||
unsigned GetConnectionCount(unsigned nodeIndex) const;
|
||||
void GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const;
|
||||
node_type GetNodeAtIndex(unsigned nodeIndex) const;
|
||||
|
||||
protected:
|
||||
void ClearDijkstra(void);
|
||||
void GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT);
|
||||
|
||||
DataStructures::Map<node_type, DataStructures::Map<node_type, weight_type> *> adjacencyLists;
|
||||
|
||||
// All these variables are for path finding with Dijkstra
|
||||
// 08/23/06 Won't compile as a DLL inside this struct
|
||||
// struct
|
||||
// {
|
||||
bool isValidPath;
|
||||
node_type rootNode;
|
||||
DataStructures::OrderedList<node_type, node_type> costMatrixIndices;
|
||||
weight_type *costMatrix;
|
||||
node_type *leastNodeArray;
|
||||
// } dijkstra;
|
||||
|
||||
struct NodeAndParent
|
||||
{
|
||||
DataStructures::Tree<node_type>*node;
|
||||
DataStructures::Tree<node_type>*parent;
|
||||
};
|
||||
};
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph()
|
||||
{
|
||||
isValidPath=false;
|
||||
costMatrix=0;
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::~WeightedGraph()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::WeightedGraph( const WeightedGraph& original_copy )
|
||||
{
|
||||
adjacencyLists=original_copy.adjacencyLists;
|
||||
|
||||
isValidPath=original_copy.isValidPath;
|
||||
if (isValidPath)
|
||||
{
|
||||
rootNode=original_copy.rootNode;
|
||||
costMatrixIndices=original_copy.costMatrixIndices;
|
||||
costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ );
|
||||
leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(costMatrixIndices.Size(), _FILE_AND_LINE_ );
|
||||
memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
|
||||
memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
|
||||
}
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
WeightedGraph<node_type, weight_type, allow_unlinkedNodes>& WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::operator=( const WeightedGraph& original_copy )
|
||||
{
|
||||
adjacencyLists=original_copy.adjacencyLists;
|
||||
|
||||
isValidPath=original_copy.isValidPath;
|
||||
if (isValidPath)
|
||||
{
|
||||
rootNode=original_copy.rootNode;
|
||||
costMatrixIndices=original_copy.costMatrixIndices;
|
||||
costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(costMatrixIndices.Size() * costMatrixIndices.Size(), _FILE_AND_LINE_ );
|
||||
leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(costMatrixIndices.Size(), _FILE_AND_LINE_ );
|
||||
memcpy(costMatrix, original_copy.costMatrix, costMatrixIndices.Size() * costMatrixIndices.Size() * sizeof(weight_type));
|
||||
memcpy(leastNodeArray, original_copy.leastNodeArray, costMatrixIndices.Size() * sizeof(weight_type));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddNode(const node_type &node)
|
||||
{
|
||||
adjacencyLists.SetNew(node, RakNet::OP_NEW<DataStructures::Map<node_type, weight_type> >( _FILE_AND_LINE_) );
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveNode(const node_type &node)
|
||||
{
|
||||
unsigned i;
|
||||
DataStructures::Queue<node_type> removeNodeQueue;
|
||||
|
||||
removeNodeQueue.Push(node, _FILE_AND_LINE_ );
|
||||
while (removeNodeQueue.Size())
|
||||
{
|
||||
RakNet::OP_DELETE(adjacencyLists.Pop(removeNodeQueue.Pop()), _FILE_AND_LINE_);
|
||||
|
||||
// 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), _FILE_AND_LINE_ );
|
||||
}
|
||||
}
|
||||
|
||||
ClearDijkstra();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::HasConnection(const node_type &node1, const node_type &node2)
|
||||
{
|
||||
if (node1==node2)
|
||||
return false;
|
||||
if (adjacencyLists.Has(node1)==false)
|
||||
return false;
|
||||
return adjacencyLists.Get(node1)->Has(node2);
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::AddConnection(const node_type &node1, const node_type &node2, weight_type weight)
|
||||
{
|
||||
if (node1==node2)
|
||||
return;
|
||||
|
||||
if (adjacencyLists.Has(node1)==false)
|
||||
AddNode(node1);
|
||||
adjacencyLists.Get(node1)->Set(node2, weight);
|
||||
if (adjacencyLists.Has(node2)==false)
|
||||
AddNode(node2);
|
||||
adjacencyLists.Get(node2)->Set(node1, weight);
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::RemoveConnection(const node_type &node1, const node_type &node2)
|
||||
{
|
||||
adjacencyLists.Get(node2)->Delete(node1);
|
||||
adjacencyLists.Get(node1)->Delete(node2);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
if (allow_unlinkedNodes==false) // If we do not allow _unlinked nodes, then if there are no connections, remove the node
|
||||
{
|
||||
if (adjacencyLists.Get(node1)->Size()==0)
|
||||
RemoveNode(node1); // Will also remove node1 from the adjacency list of node2
|
||||
if (adjacencyLists.Has(node2) && adjacencyLists.Get(node2)->Size()==0)
|
||||
RemoveNode(node2);
|
||||
}
|
||||
|
||||
ClearDijkstra();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Clear(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < adjacencyLists.Size(); i++)
|
||||
RakNet::OP_DELETE(adjacencyLists[i], _FILE_AND_LINE_);
|
||||
adjacencyLists.Clear();
|
||||
|
||||
ClearDijkstra();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetShortestPath(DataStructures::List<node_type> &path, node_type startNode, node_type endNode, weight_type INFINITE_WEIGHT)
|
||||
{
|
||||
path.Clear(false, _FILE_AND_LINE_);
|
||||
if (startNode==endNode)
|
||||
{
|
||||
path.Insert(startNode, _FILE_AND_LINE_);
|
||||
path.Insert(endNode, _FILE_AND_LINE_);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isValidPath==false || rootNode!=startNode)
|
||||
{
|
||||
ClearDijkstra();
|
||||
GenerateDisjktraMatrix(startNode, INFINITE_WEIGHT);
|
||||
}
|
||||
|
||||
// return the results
|
||||
bool objectExists;
|
||||
unsigned col,row;
|
||||
weight_type currentWeight;
|
||||
DataStructures::Queue<node_type> outputQueue;
|
||||
col=costMatrixIndices.GetIndexFromKey(endNode, &objectExists);
|
||||
if (costMatrixIndices.Size()<2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (objectExists==false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
node_type vertex;
|
||||
row=costMatrixIndices.Size()-2;
|
||||
if (row==0)
|
||||
{
|
||||
path.Insert(startNode, _FILE_AND_LINE_);
|
||||
path.Insert(endNode, _FILE_AND_LINE_);
|
||||
return true;
|
||||
}
|
||||
currentWeight=costMatrix[row*adjacencyLists.Size() + col];
|
||||
if (currentWeight==INFINITE_WEIGHT)
|
||||
{
|
||||
// No path
|
||||
return true;
|
||||
}
|
||||
vertex=endNode;
|
||||
outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_);
|
||||
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, _FILE_AND_LINE_);
|
||||
while (!outputQueue.Empty())
|
||||
path.Insert(outputQueue.Pop(), _FILE_AND_LINE_);
|
||||
return true;
|
||||
}
|
||||
--row;
|
||||
}
|
||||
|
||||
vertex=leastNodeArray[row];
|
||||
outputQueue.PushAtHead(vertex, 0, _FILE_AND_LINE_);
|
||||
if (row==0)
|
||||
break;
|
||||
col=costMatrixIndices.GetIndexFromKey(vertex, &objectExists);
|
||||
currentWeight=costMatrix[row*adjacencyLists.Size() + col];
|
||||
}
|
||||
|
||||
path.Insert(startNode, _FILE_AND_LINE_);
|
||||
while (!outputQueue.Empty())
|
||||
path.Insert(outputQueue.Pop(), _FILE_AND_LINE_);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
node_type WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeAtIndex(unsigned nodeIndex) const
|
||||
{
|
||||
return adjacencyLists.GetKeyAtIndex(nodeIndex);
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetNodeCount(void) const
|
||||
{
|
||||
return adjacencyLists.Size();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
unsigned WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionCount(unsigned nodeIndex) const
|
||||
{
|
||||
return adjacencyLists[nodeIndex]->Size();
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetConnectionAtIndex(unsigned nodeIndex, unsigned connectionIndex, node_type &outNode, weight_type &outWeight) const
|
||||
{
|
||||
outWeight=adjacencyLists[nodeIndex]->operator[](connectionIndex);
|
||||
outNode=adjacencyLists[nodeIndex]->GetKeyAtIndex(connectionIndex);
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
bool WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GetSpanningTree(DataStructures::Tree<node_type> &outTree, DataStructures::List<node_type> *inputNodes, node_type startNode, weight_type INFINITE_WEIGHT )
|
||||
{
|
||||
// Find the shortest path from the start node to each of the input nodes. Add this path to a new WeightedGraph if the result is reachable
|
||||
DataStructures::List<node_type> path;
|
||||
DataStructures::WeightedGraph<node_type, weight_type, allow_unlinkedNodes> outGraph;
|
||||
bool res;
|
||||
unsigned i,j;
|
||||
for (i=0; i < inputNodes->Size(); i++)
|
||||
{
|
||||
res=GetShortestPath(path, startNode, (*inputNodes)[i], INFINITE_WEIGHT);
|
||||
if (res && path.Size()>0)
|
||||
{
|
||||
for (j=0; j < path.Size()-1; j++)
|
||||
{
|
||||
// Don't bother looking up the weight
|
||||
outGraph.AddConnection(path[j], path[j+1], INFINITE_WEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the graph to a tree.
|
||||
DataStructures::Queue<NodeAndParent> nodesToProcess;
|
||||
DataStructures::Tree<node_type> *current;
|
||||
DataStructures::Map<node_type, weight_type> *adjacencyList;
|
||||
node_type key;
|
||||
NodeAndParent nap, nap2;
|
||||
outTree.DeleteDecendants();
|
||||
outTree.data=startNode;
|
||||
current=&outTree;
|
||||
if (outGraph.adjacencyLists.Has(startNode)==false)
|
||||
return false;
|
||||
adjacencyList = outGraph.adjacencyLists.Get(startNode);
|
||||
|
||||
for (i=0; i < adjacencyList->Size(); i++)
|
||||
{
|
||||
nap2.node=RakNet::OP_NEW<DataStructures::Tree<node_type> >( _FILE_AND_LINE_ );
|
||||
nap2.node->data=adjacencyList->GetKeyAtIndex(i);
|
||||
nap2.parent=current;
|
||||
nodesToProcess.Push(nap2, _FILE_AND_LINE_ );
|
||||
current->children.Insert(nap2.node, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
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<DataStructures::Tree<node_type> >( _FILE_AND_LINE_ );
|
||||
nap2.node->data=key;
|
||||
nap2.parent=current;
|
||||
nodesToProcess.Push(nap2, _FILE_AND_LINE_ );
|
||||
current->children.Insert(nap2.node, _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::GenerateDisjktraMatrix(node_type startNode, weight_type INFINITE_WEIGHT)
|
||||
{
|
||||
if (adjacencyLists.Size()==0)
|
||||
return;
|
||||
|
||||
costMatrix = RakNet::OP_NEW_ARRAY<weight_type>(adjacencyLists.Size() * adjacencyLists.Size(), _FILE_AND_LINE_ );
|
||||
leastNodeArray = RakNet::OP_NEW_ARRAY<node_type>(adjacencyLists.Size(), _FILE_AND_LINE_ );
|
||||
|
||||
node_type currentNode;
|
||||
unsigned col, row, row2, openSetIndex;
|
||||
node_type adjacentKey;
|
||||
unsigned adjacentIndex;
|
||||
weight_type edgeWeight, currentNodeWeight, adjacentNodeWeight;
|
||||
DataStructures::Map<node_type, weight_type> *adjacencyList;
|
||||
DataStructures::Heap<weight_type, node_type, false> minHeap;
|
||||
DataStructures::Map<node_type, weight_type> openSet;
|
||||
|
||||
for (col=0; col < adjacencyLists.Size(); col++)
|
||||
{
|
||||
// This should be already sorted, so it's a bit inefficient to do an insertion sort, but what the heck
|
||||
costMatrixIndices.Insert(adjacencyLists.GetKeyAtIndex(col),adjacencyLists.GetKeyAtIndex(col), true, _FILE_AND_LINE_);
|
||||
}
|
||||
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(true,_FILE_AND_LINE_);
|
||||
for (openSetIndex=0; openSetIndex < openSet.Size(); openSetIndex++)
|
||||
minHeap.Push(openSet[openSetIndex], openSet.GetKeyAtIndex(openSetIndex),_FILE_AND_LINE_);
|
||||
|
||||
/*
|
||||
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 <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::ClearDijkstra(void)
|
||||
{
|
||||
if (isValidPath)
|
||||
{
|
||||
isValidPath=false;
|
||||
RakNet::OP_DELETE_ARRAY(costMatrix, _FILE_AND_LINE_);
|
||||
RakNet::OP_DELETE_ARRAY(leastNodeArray, _FILE_AND_LINE_);
|
||||
costMatrixIndices.Clear(false, _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
|
||||
template <class node_type, class weight_type, bool allow_unlinkedNodes>
|
||||
void WeightedGraph<node_type, weight_type, allow_unlinkedNodes>::Print(void)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
unsigned i,j;
|
||||
for (i=0; i < adjacencyLists.Size(); i++)
|
||||
{
|
||||
//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("<Empty>");
|
||||
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
|
||||
63
third-party/RakNet/src/RakNet/DataCompressor.cpp
vendored
Normal file
63
third-party/RakNet/src/RakNet/DataCompressor.cpp
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "DataCompressor.hpp"
|
||||
#include "DS_HuffmanEncodingTree.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include <string.h> // Use string.h rather than memory.h for a console
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(DataCompressor,DataCompressor)
|
||||
|
||||
void DataCompressor::Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output )
|
||||
{
|
||||
// Don't use this for small files as you will just make them bigger!
|
||||
RakAssert(sizeInBytes > 2048);
|
||||
|
||||
unsigned int frequencyTable[ 256 ];
|
||||
unsigned int i;
|
||||
memset(frequencyTable,0,256*sizeof(unsigned int));
|
||||
for (i=0; i < sizeInBytes; i++)
|
||||
++frequencyTable[userData[i]];
|
||||
HuffmanEncodingTree tree;
|
||||
BitSize_t writeOffset1, writeOffset2, bitsUsed1, bitsUsed2;
|
||||
tree.GenerateFromFrequencyTable(frequencyTable);
|
||||
output->WriteCompressed(sizeInBytes);
|
||||
for (i=0; i < 256; i++)
|
||||
output->WriteCompressed(frequencyTable[i]);
|
||||
output->AlignWriteToByteBoundary();
|
||||
writeOffset1=output->GetWriteOffset();
|
||||
output->Write((unsigned int)0); // Dummy value
|
||||
bitsUsed1=output->GetNumberOfBitsUsed();
|
||||
tree.EncodeArray(userData, sizeInBytes, output);
|
||||
bitsUsed2=output->GetNumberOfBitsUsed();
|
||||
writeOffset2=output->GetWriteOffset();
|
||||
output->SetWriteOffset(writeOffset1);
|
||||
output->Write(bitsUsed2-bitsUsed1); // Go back and write how many bits were used for the encoding
|
||||
output->SetWriteOffset(writeOffset2);
|
||||
}
|
||||
|
||||
unsigned DataCompressor::DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output )
|
||||
{
|
||||
HuffmanEncodingTree tree;
|
||||
unsigned int bitsUsed, destinationSizeInBytes;
|
||||
unsigned int decompressedBytes;
|
||||
unsigned int frequencyTable[ 256 ];
|
||||
unsigned i;
|
||||
|
||||
input->ReadCompressed(destinationSizeInBytes);
|
||||
for (i=0; i < 256; i++)
|
||||
input->ReadCompressed(frequencyTable[i]);
|
||||
input->AlignReadToByteBoundary();
|
||||
if (input->Read(bitsUsed)==false)
|
||||
{
|
||||
// Read error
|
||||
#ifdef _DEBUG
|
||||
RakAssert(0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
*output = (unsigned char*) rakMalloc_Ex(destinationSizeInBytes, _FILE_AND_LINE_);
|
||||
tree.GenerateFromFrequencyTable(frequencyTable);
|
||||
decompressedBytes=tree.DecodeArray(input, bitsUsed, destinationSizeInBytes, *output );
|
||||
RakAssert(decompressedBytes==destinationSizeInBytes);
|
||||
return destinationSizeInBytes;
|
||||
}
|
||||
33
third-party/RakNet/src/RakNet/DataCompressor.hpp
vendored
Normal file
33
third-party/RakNet/src/RakNet/DataCompressor.hpp
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
/// \file DataCompressor.h
|
||||
/// \brief DataCompressor does compression on a block of data.
|
||||
/// \details 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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __DATA_COMPRESSOR_H
|
||||
#define __DATA_COMPRESSOR_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "DS_HuffmanEncodingTree.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
/// \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:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(DataCompressor)
|
||||
|
||||
static void Compress( unsigned char *userData, unsigned sizeInBytes, RakNet::BitStream * output );
|
||||
static unsigned DecompressAndAllocate( RakNet::BitStream * input, unsigned char **output );
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
242
third-party/RakNet/src/RakNet/DirectoryDeltaTransfer.cpp
vendored
Normal file
242
third-party/RakNet/src/RakNet/DirectoryDeltaTransfer.cpp
vendored
Normal file
@@ -0,0 +1,242 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#include "DirectoryDeltaTransfer.hpp"
|
||||
#include "FileList.hpp"
|
||||
#include "StringCompressor.hpp"
|
||||
#include "RakPeerInterface.hpp"
|
||||
#include "FileListTransfer.hpp"
|
||||
#include "FileListTransferCBInterface.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "MessageIdentifiers.hpp"
|
||||
#include "FileOperations.hpp"
|
||||
#include "IncrementalReadInterface.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
class DDTCallback : public FileListTransferCBInterface
|
||||
{
|
||||
public:
|
||||
unsigned subdirLen;
|
||||
char outputSubdir[512];
|
||||
FileListTransferCBInterface *onFileCallback;
|
||||
|
||||
DDTCallback() {}
|
||||
virtual ~DDTCallback() {}
|
||||
|
||||
virtual bool OnFile(OnFileStruct *onFileStruct)
|
||||
{
|
||||
char fullPathToDir[1024];
|
||||
|
||||
if (onFileStruct->fileName && onFileStruct->fileData && subdirLen < strlen(onFileStruct->fileName))
|
||||
{
|
||||
strcpy(fullPathToDir, outputSubdir);
|
||||
strcat(fullPathToDir, onFileStruct->fileName+subdirLen);
|
||||
WriteFileWithDirectories(fullPathToDir, (char*)onFileStruct->fileData, (unsigned int ) onFileStruct->byteLengthOfThisFile);
|
||||
}
|
||||
else
|
||||
fullPathToDir[0]=0;
|
||||
|
||||
return onFileCallback->OnFile(onFileStruct);
|
||||
}
|
||||
|
||||
virtual void OnFileProgress(FileProgressStruct *fps)
|
||||
{
|
||||
char fullPathToDir[1024];
|
||||
|
||||
if (fps->onFileStruct->fileName && subdirLen < strlen(fps->onFileStruct->fileName))
|
||||
{
|
||||
strcpy(fullPathToDir, outputSubdir);
|
||||
strcat(fullPathToDir, fps->onFileStruct->fileName+subdirLen);
|
||||
}
|
||||
else
|
||||
fullPathToDir[0]=0;
|
||||
|
||||
onFileCallback->OnFileProgress(fps);
|
||||
}
|
||||
virtual bool OnDownloadComplete(DownloadCompleteStruct *dcs)
|
||||
{
|
||||
return onFileCallback->OnDownloadComplete(dcs);
|
||||
}
|
||||
};
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(DirectoryDeltaTransfer,DirectoryDeltaTransfer);
|
||||
|
||||
DirectoryDeltaTransfer::DirectoryDeltaTransfer()
|
||||
{
|
||||
applicationDirectory[0]=0;
|
||||
fileListTransfer=0;
|
||||
availableUploads = RakNet::OP_NEW<FileList>( _FILE_AND_LINE_ );
|
||||
priority=HIGH_PRIORITY;
|
||||
orderingChannel=0;
|
||||
incrementalReadInterface=0;
|
||||
}
|
||||
DirectoryDeltaTransfer::~DirectoryDeltaTransfer()
|
||||
{
|
||||
RakNet::OP_DELETE(availableUploads, _FILE_AND_LINE_);
|
||||
}
|
||||
void DirectoryDeltaTransfer::SetFileListTransferPlugin(FileListTransfer *flt)
|
||||
{
|
||||
if (fileListTransfer)
|
||||
{
|
||||
DataStructures::List<FileListProgress*> fileListProgressList;
|
||||
fileListTransfer->GetCallbacks(fileListProgressList);
|
||||
unsigned int i;
|
||||
for (i=0; i < fileListProgressList.Size(); i++)
|
||||
availableUploads->RemoveCallback(fileListProgressList[i]);
|
||||
}
|
||||
|
||||
fileListTransfer=flt;
|
||||
|
||||
if (flt)
|
||||
{
|
||||
DataStructures::List<FileListProgress*> fileListProgressList;
|
||||
flt->GetCallbacks(fileListProgressList);
|
||||
unsigned int i;
|
||||
for (i=0; i < fileListProgressList.Size(); i++)
|
||||
availableUploads->AddCallback(fileListProgressList[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
availableUploads->ClearCallbacks();
|
||||
}
|
||||
}
|
||||
void DirectoryDeltaTransfer::SetApplicationDirectory(const char *pathToApplication)
|
||||
{
|
||||
if (pathToApplication==0 || pathToApplication[0]==0)
|
||||
applicationDirectory[0]=0;
|
||||
else
|
||||
{
|
||||
strncpy(applicationDirectory, pathToApplication, 510);
|
||||
if (applicationDirectory[strlen(applicationDirectory)-1]!='/' && applicationDirectory[strlen(applicationDirectory)-1]!='\\')
|
||||
strcat(applicationDirectory, "/");
|
||||
applicationDirectory[511]=0;
|
||||
}
|
||||
}
|
||||
void DirectoryDeltaTransfer::SetUploadSendParameters(PacketPriority _priority, char _orderingChannel)
|
||||
{
|
||||
priority=_priority;
|
||||
orderingChannel=_orderingChannel;
|
||||
}
|
||||
void DirectoryDeltaTransfer::AddUploadsFromSubdirectory(const char *subdir)
|
||||
{
|
||||
availableUploads->AddFilesFromDirectory(applicationDirectory, subdir, true, false, true, FileListNodeContext(0,0));
|
||||
}
|
||||
unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb)
|
||||
{
|
||||
RakAssert(host!=UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
DDTCallback *transferCallback;
|
||||
|
||||
localFiles.AddCallback(cb);
|
||||
|
||||
// Prepare the callback data
|
||||
transferCallback = RakNet::OP_NEW<DDTCallback>( _FILE_AND_LINE_ );
|
||||
if (subdir && subdir[0])
|
||||
{
|
||||
transferCallback->subdirLen=(unsigned int)strlen(subdir);
|
||||
if (subdir[transferCallback->subdirLen-1]!='/' && subdir[transferCallback->subdirLen-1]!='\\')
|
||||
transferCallback->subdirLen++;
|
||||
}
|
||||
else
|
||||
transferCallback->subdirLen=0;
|
||||
if (prependAppDirToOutputSubdir)
|
||||
strcpy(transferCallback->outputSubdir, applicationDirectory);
|
||||
else
|
||||
transferCallback->outputSubdir[0]=0;
|
||||
if (outputSubdir)
|
||||
strcat(transferCallback->outputSubdir, outputSubdir);
|
||||
if (transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='/' && transferCallback->outputSubdir[strlen(transferCallback->outputSubdir)-1]!='\\')
|
||||
strcat(transferCallback->outputSubdir, "/");
|
||||
transferCallback->onFileCallback=onFileCallback;
|
||||
|
||||
// Setup the transfer plugin to get the response to this download request
|
||||
unsigned short setId = fileListTransfer->SetupReceive(transferCallback, true, host);
|
||||
|
||||
// Send to the host, telling it to process this request
|
||||
RakNet::BitStream outBitstream;
|
||||
outBitstream.Write((MessageID)ID_DDT_DOWNLOAD_REQUEST);
|
||||
outBitstream.Write(setId);
|
||||
StringCompressor::Instance()->EncodeString(subdir, 256, &outBitstream);
|
||||
StringCompressor::Instance()->EncodeString(outputSubdir, 256, &outBitstream);
|
||||
localFiles.Serialize(&outBitstream);
|
||||
SendUnified(&outBitstream, _priority, RELIABLE_ORDERED, _orderingChannel, host, false);
|
||||
|
||||
return setId;
|
||||
}
|
||||
unsigned short DirectoryDeltaTransfer::DownloadFromSubdirectory(const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb)
|
||||
{
|
||||
FileList localFiles;
|
||||
// Get a hash of all the files that we already have (if any)
|
||||
localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0));
|
||||
return DownloadFromSubdirectory(localFiles, subdir, outputSubdir, prependAppDirToOutputSubdir, host, onFileCallback, _priority, _orderingChannel, cb);
|
||||
}
|
||||
void DirectoryDeltaTransfer::GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir)
|
||||
{
|
||||
localFiles.AddFilesFromDirectory(prependAppDirToOutputSubdir ? applicationDirectory : 0, outputSubdir, true, false, true, FileListNodeContext(0,0));
|
||||
}
|
||||
void DirectoryDeltaTransfer::ClearUploads(void)
|
||||
{
|
||||
availableUploads->Clear();
|
||||
}
|
||||
void DirectoryDeltaTransfer::OnDownloadRequest(Packet *packet)
|
||||
{
|
||||
char subdir[256];
|
||||
char remoteSubdir[256];
|
||||
RakNet::BitStream inBitstream(packet->data, packet->length, false);
|
||||
FileList remoteFileHash;
|
||||
FileList delta;
|
||||
unsigned short setId;
|
||||
inBitstream.IgnoreBits(8);
|
||||
inBitstream.Read(setId);
|
||||
StringCompressor::Instance()->DecodeString(subdir, 256, &inBitstream);
|
||||
StringCompressor::Instance()->DecodeString(remoteSubdir, 256, &inBitstream);
|
||||
if (remoteFileHash.Deserialize(&inBitstream)==false)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(0);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
availableUploads->GetDeltaToCurrent(&remoteFileHash, &delta, subdir, remoteSubdir);
|
||||
if (incrementalReadInterface==0)
|
||||
delta.PopulateDataFromDisk(applicationDirectory, true, false, true);
|
||||
else
|
||||
delta.FlagFilesAsReferences();
|
||||
|
||||
// This will call the ddtCallback interface that was passed to FileListTransfer::SetupReceive on the remote system
|
||||
fileListTransfer->Send(&delta, rakPeerInterface, packet->systemAddress, setId, priority, orderingChannel, incrementalReadInterface, chunkSize);
|
||||
}
|
||||
PluginReceiveResult DirectoryDeltaTransfer::OnReceive(Packet *packet)
|
||||
{
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_DDT_DOWNLOAD_REQUEST:
|
||||
OnDownloadRequest(packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
}
|
||||
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
|
||||
unsigned DirectoryDeltaTransfer::GetNumberOfFilesForUpload(void) const
|
||||
{
|
||||
return availableUploads->fileList.Size();
|
||||
}
|
||||
|
||||
void DirectoryDeltaTransfer::SetDownloadRequestIncrementalReadInterface(IncrementalReadInterface *_incrementalReadInterface, unsigned int _chunkSize)
|
||||
{
|
||||
incrementalReadInterface=_incrementalReadInterface;
|
||||
chunkSize=_chunkSize;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
164
third-party/RakNet/src/RakNet/DirectoryDeltaTransfer.hpp
vendored
Normal file
164
third-party/RakNet/src/RakNet/DirectoryDeltaTransfer.hpp
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
/// \file DirectoryDeltaTransfer.h
|
||||
/// \brief Simple class to send changes between directories.
|
||||
/// \details In essence, a simple autopatcher that can be used for transmitting levels, skins, etc.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_DirectoryDeltaTransfer==1 && _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#ifndef __DIRECTORY_DELTA_TRANSFER_H
|
||||
#define __DIRECTORY_DELTA_TRANSFER_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "DS_Map.hpp"
|
||||
#include "PacketPriority.hpp"
|
||||
|
||||
/// \defgroup DIRECTORY_DELTA_TRANSFER_GROUP DirectoryDeltaTransfer
|
||||
/// \brief Simple class to send changes between directories
|
||||
/// \details
|
||||
/// \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.
|
||||
/// \details
|
||||
/// \sa AutopatcherClient 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/"<BR>
|
||||
/// To allow other systems to download files, call AddUploadsFromSubdirectory, where the parameter is a path relative<BR>
|
||||
/// to the path to your application. This includes subdirectories.<BR>
|
||||
/// For example:<BR>
|
||||
/// SetApplicationDirectory("C:/Games/MyRPG/");<BR>
|
||||
/// AddUploadsFromSubdirectory("Mods/Skins/");<BR>
|
||||
/// would allow downloads from<BR>
|
||||
/// "C:/Games/MyRPG/Mods/Skins/*.*" as well as "C:/Games/MyRPG/Mods/Skins/Level1/*.*"<BR>
|
||||
/// It would NOT allow downloads from C:/Games/MyRPG/Levels, nor would it allow downloads from C:/Windows<BR>
|
||||
/// While pathToApplication can be anything you want, applicationSubdirectory must match either partially or fully between systems.
|
||||
/// \ingroup DIRECTORY_DELTA_TRANSFER_GROUP
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
class FileList;
|
||||
struct Packet;
|
||||
struct InternalPacket;
|
||||
struct DownloadRequest;
|
||||
class FileListTransfer;
|
||||
class FileListTransferCBInterface;
|
||||
class FileListProgress;
|
||||
class IncrementalReadInterface;
|
||||
|
||||
class RAK_DLL_EXPORT DirectoryDeltaTransfer : public PluginInterface2
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(DirectoryDeltaTransfer)
|
||||
|
||||
// Constructor
|
||||
DirectoryDeltaTransfer();
|
||||
|
||||
// Destructor
|
||||
virtual ~DirectoryDeltaTransfer();
|
||||
|
||||
/// \brief This plugin has a dependency on the FileListTransfer plugin, which it uses to actually send the files.
|
||||
/// \details 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);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief Add all files in the specified subdirectory recursively.
|
||||
/// \details \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);
|
||||
|
||||
/// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory.
|
||||
/// \details \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
|
||||
/// \note Blocking. Will block while hashes of the local files are generated
|
||||
/// \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);
|
||||
|
||||
/// \brief Downloads files from the matching parameter \a subdir in AddUploadsFromSubdirectory.
|
||||
/// \details \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
|
||||
/// \note Nonblocking, but requires call to GenerateHashes()
|
||||
/// \param[in] localFiles Hashes of local files already on the harddrive. Populate with GenerateHashes(), which you may wish to call from a thread
|
||||
/// \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(FileList &localFiles, const char *subdir, const char *outputSubdir, bool prependAppDirToOutputSubdir, SystemAddress host, FileListTransferCBInterface *onFileCallback, PacketPriority _priority, char _orderingChannel, FileListProgress *cb);
|
||||
|
||||
/// Hash files already on the harddrive, in preparation for a call to DownloadFromSubdirectory(). Passed to second version of DownloadFromSubdirectory()
|
||||
/// This is slow, and it is exposed so you can call it from a thread before calling DownloadFromSubdirectory()
|
||||
/// \param[out] localFiles List of hashed files populated from \a outputSubdir and \a prependAppDirToOutputSubdir
|
||||
/// \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.
|
||||
void GenerateHashes(FileList &localFiles, const char *outputSubdir, bool prependAppDirToOutputSubdir);
|
||||
|
||||
/// \brief Clear all allowed uploads previously set with AddUploadsFromSubdirectory
|
||||
void ClearUploads(void);
|
||||
|
||||
/// \brief Returns how many files are available for upload
|
||||
/// \return How many files are available for upload
|
||||
unsigned GetNumberOfFilesForUpload(void) const;
|
||||
|
||||
/// \brief Normally, if a remote system requests files, those files are all loaded into memory and sent immediately.
|
||||
/// \details 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 PluginReceiveResult OnReceive(Packet *packet);
|
||||
protected:
|
||||
void OnDownloadRequest(Packet *packet);
|
||||
|
||||
char applicationDirectory[512];
|
||||
FileListTransfer *fileListTransfer;
|
||||
FileList *availableUploads;
|
||||
PacketPriority priority;
|
||||
char orderingChannel;
|
||||
IncrementalReadInterface *incrementalReadInterface;
|
||||
unsigned int chunkSize;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
236
third-party/RakNet/src/RakNet/DynDNS.cpp
vendored
Normal file
236
third-party/RakNet/src/RakNet/DynDNS.cpp
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
|
||||
#include "TCPInterface.hpp"
|
||||
#include "SocketLayer.hpp"
|
||||
#include "DynDNS.hpp"
|
||||
#include "GetTime.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
struct DynDnsResult
|
||||
{
|
||||
const char *description;
|
||||
const char *code;
|
||||
DynDnsResultCode resultCode;
|
||||
};
|
||||
|
||||
DynDnsResult resultTable[13] =
|
||||
{
|
||||
// See http://www.dyndns.com/developers/specs/flow.pdf
|
||||
{"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update!
|
||||
{"No change", "nochg", RC_NO_CHANGE},
|
||||
{"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE},
|
||||
{"Useragent is blocked", "badagent", RC_BAD_AGENT},
|
||||
{"Username/password pair bad", "badauth", RC_BAD_AUTH},
|
||||
{"Bad system parameter", "badsys", RC_BAD_SYS},
|
||||
{"DNS inconsistency", "dnserr", RC_DNS_ERROR},
|
||||
{"Paid account feature", "!donator", RC_NOT_DONATOR},
|
||||
{"No such host in system", "nohost", RC_NO_HOST},
|
||||
{"Invalid hostname format", "notfqdn", RC_NOT_FQDN},
|
||||
{"Serious error", "numhost", RC_NUM_HOST},
|
||||
{"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS},
|
||||
{"911", "911", RC_911},
|
||||
};
|
||||
DynDNS::DynDNS()
|
||||
{
|
||||
connectPhase=CP_IDLE;
|
||||
tcp=0;
|
||||
}
|
||||
DynDNS::~DynDNS()
|
||||
{
|
||||
if (tcp)
|
||||
RakNet::OP_DELETE(tcp, _FILE_AND_LINE_);
|
||||
}
|
||||
void DynDNS::Stop(void)
|
||||
{
|
||||
tcp->Stop();
|
||||
connectPhase = CP_IDLE;
|
||||
RakNet::OP_DELETE(tcp, _FILE_AND_LINE_);
|
||||
tcp=0;
|
||||
}
|
||||
|
||||
|
||||
// newIPAddress is optional - if left out, DynDNS will use whatever it receives
|
||||
void DynDNS::UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword )
|
||||
{
|
||||
myIPStr[0]=0;
|
||||
|
||||
if (tcp==0)
|
||||
tcp = RakNet::OP_NEW<TCPInterface>(_FILE_AND_LINE_);
|
||||
connectPhase = CP_IDLE;
|
||||
host = dnsHost;
|
||||
|
||||
if (tcp->Start(0, 1)==false)
|
||||
{
|
||||
SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start");
|
||||
return;
|
||||
}
|
||||
|
||||
connectPhase = CP_CONNECTING_TO_CHECKIP;
|
||||
tcp->Connect("checkip.dyndns.org", 80, false);
|
||||
|
||||
// See https://www.dyndns.com/developers/specs/syntax.html
|
||||
getString="GET /nic/update?hostname=";
|
||||
getString+=dnsHost;
|
||||
if (newIPAddress)
|
||||
{
|
||||
getString+="&myip=";
|
||||
getString+=newIPAddress;
|
||||
}
|
||||
getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n";
|
||||
getString+="Host: members.dyndns.org\n";
|
||||
getString+="Authorization: Basic ";
|
||||
char outputData[512];
|
||||
TCPInterface::Base64Encoding(usernameAndPassword, (int) strlen(usernameAndPassword), outputData);
|
||||
getString+=outputData;
|
||||
getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n";
|
||||
}
|
||||
void DynDNS::Update(void)
|
||||
{
|
||||
if (connectPhase==CP_IDLE)
|
||||
return;
|
||||
|
||||
serverAddress=tcp->HasFailedConnectionAttempt();
|
||||
if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS");
|
||||
return;
|
||||
}
|
||||
|
||||
serverAddress=tcp->HasCompletedConnectionAttempt();
|
||||
if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
if (connectPhase == CP_CONNECTING_TO_CHECKIP)
|
||||
{
|
||||
checkIpAddress=serverAddress;
|
||||
connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE;
|
||||
tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time
|
||||
}
|
||||
else
|
||||
{
|
||||
connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE;
|
||||
tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false);
|
||||
}
|
||||
phaseTimeout=RakNet::GetTime()+1000;
|
||||
}
|
||||
|
||||
if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && RakNet::GetTime()>phaseTimeout)
|
||||
{
|
||||
connectPhase = CP_CONNECTING_TO_DYNDNS;
|
||||
tcp->CloseConnection(checkIpAddress);
|
||||
tcp->Connect("members.dyndns.org", 80, false);
|
||||
}
|
||||
else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && RakNet::GetTime()>phaseTimeout)
|
||||
{
|
||||
SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond");
|
||||
return;
|
||||
}
|
||||
|
||||
Packet *packet = tcp->Receive();
|
||||
if (packet)
|
||||
{
|
||||
if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
char *result;
|
||||
result=strstr((char*) packet->data, "Connection: close");
|
||||
if (result!=0)
|
||||
{
|
||||
result+=strlen("Connection: close");
|
||||
while (*result && (*result=='\r') || (*result=='\n') || (*result==' ') )
|
||||
result++;
|
||||
for (i=0; i < 13; i++)
|
||||
{
|
||||
if (strncmp(resultTable[i].code, result, strlen(resultTable[i].code))==0)
|
||||
{
|
||||
if (resultTable[i].resultCode==RC_SUCCESS)
|
||||
{
|
||||
// Read my external IP into myIPStr
|
||||
// Advance until we hit a number
|
||||
while (*result && ((*result<'0') || (*result>'9')) )
|
||||
result++;
|
||||
if (*result)
|
||||
{
|
||||
SystemAddress parser;
|
||||
parser.FromString(result);
|
||||
parser.ToString(false, myIPStr);
|
||||
}
|
||||
}
|
||||
tcp->DeallocatePacket(packet);
|
||||
SetCompleted(resultTable[i].resultCode, resultTable[i].description);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i==13)
|
||||
{
|
||||
tcp->DeallocatePacket(packet);
|
||||
SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tcp->DeallocatePacket(packet);
|
||||
SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/html
|
||||
Server: DynDNS-CheckIP/1.0
|
||||
Connection: close
|
||||
Cache-Control: no-cache
|
||||
Pragma: no-cache
|
||||
Content-Length: 105
|
||||
|
||||
<html><head><title>Current IP Check</title></head><body>Current IP Address: 98.1
|
||||
89.219.22</body></html>
|
||||
|
||||
|
||||
Connection to host lost.
|
||||
*/
|
||||
|
||||
char *result;
|
||||
result=strstr((char*) packet->data, "Current IP Address: ");
|
||||
if (result!=0)
|
||||
{
|
||||
result+=strlen("Current IP Address: ");
|
||||
SystemAddress myIp;
|
||||
myIp.FromString(result);
|
||||
myIp.ToString(false, myIPStr);
|
||||
|
||||
// Resolve DNS we are setting. If equal to current then abort
|
||||
const char *existingHost = ( char* ) SocketLayer::DomainNameToIP( host.C_String() );
|
||||
if (existingHost && strcmp(existingHost, myIPStr)==0)
|
||||
{
|
||||
// DynDNS considers setting the IP to what it is already set abuse
|
||||
tcp->DeallocatePacket(packet);
|
||||
SetCompleted(RC_DNS_ALREADY_SET, "No action needed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tcp->DeallocatePacket(packet);
|
||||
tcp->CloseConnection(packet->systemAddress);
|
||||
|
||||
connectPhase = CP_CONNECTING_TO_DYNDNS;
|
||||
tcp->Connect("members.dyndns.org", 80, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE)
|
||||
{
|
||||
SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // _RAKNET_SUPPORT_DynDNS
|
||||
100
third-party/RakNet/src/RakNet/DynDNS.hpp
vendored
Normal file
100
third-party/RakNet/src/RakNet/DynDNS.hpp
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
/// \file DynDNS.h
|
||||
/// \brief Helper to class to update DynDNS
|
||||
/// This can be used to determine what permissions are should be allowed to the other system
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
|
||||
class TCPInterface;
|
||||
|
||||
#ifndef __DYN_DNS_H
|
||||
#define __DYN_DNS_H
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
enum DynDnsResultCode
|
||||
{
|
||||
// ----- Success -----
|
||||
RC_SUCCESS,
|
||||
RC_DNS_ALREADY_SET, // RakNet detects no action is needed
|
||||
|
||||
// ----- Ignorable failure (treat same as success) -----
|
||||
RC_NO_CHANGE, // DynDNS detects no action is needed (treated as abuse though)
|
||||
|
||||
// ----- User error -----
|
||||
RC_NOT_DONATOR, // You have to pay to do this
|
||||
RC_NO_HOST, // This host does not exist at all
|
||||
RC_BAD_AUTH, // You set the wrong password
|
||||
RC_NOT_YOURS, // This is not your host
|
||||
|
||||
// ----- Permanent failure -----
|
||||
RC_ABUSE, // Your host has been blocked, too many failures disable your account
|
||||
RC_TCP_FAILED_TO_START, // TCP port already in use
|
||||
RC_TCP_DID_NOT_CONNECT, // DynDNS down?
|
||||
RC_UNKNOWN_RESULT, // DynDNS returned a result code that was not documented as of 12/4/2010 on http://www.dyndns.com/developers/specs/flow.pdf
|
||||
RC_PARSING_FAILURE, // Can't read the result returned, format change?
|
||||
RC_CONNECTION_LOST_WITHOUT_RESPONSE, // Lost the connection to DynDNS while communicating
|
||||
RC_BAD_AGENT, // ???
|
||||
RC_BAD_SYS, // ???
|
||||
RC_DNS_ERROR, // ???
|
||||
RC_NOT_FQDN, // ???
|
||||
RC_NUM_HOST, // ???
|
||||
RC_911, // ???
|
||||
RC_DYNDNS_TIMEOUT, // DynDNS did not respond
|
||||
};
|
||||
|
||||
// Can only process one at a time with the current implementation
|
||||
class RAK_DLL_EXPORT DynDNS
|
||||
{
|
||||
public:
|
||||
DynDNS();
|
||||
~DynDNS();
|
||||
|
||||
// Pass 0 for newIPAddress to autodetect whatever you are uploading from
|
||||
// usernameAndPassword should be in the format username:password
|
||||
void UpdateHostIP(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword );
|
||||
void Update(void);
|
||||
|
||||
// Output
|
||||
bool IsRunning(void) const {return connectPhase!=CP_IDLE;}
|
||||
bool IsCompleted(void) const {return connectPhase==CP_IDLE;}
|
||||
RakNet::DynDnsResultCode GetCompletedResultCode(void) {return result;}
|
||||
const char *GetCompletedDescription(void) const {return resultDescription;}
|
||||
bool WasResultSuccessful(void) const {return result==RC_SUCCESS || result==RC_DNS_ALREADY_SET || result==RC_NO_CHANGE;}
|
||||
char *GetMyPublicIP(void) const {return (char*) myIPStr;} // We get our public IP as part of the process. This is valid once completed
|
||||
|
||||
protected:
|
||||
void Stop(void);
|
||||
void SetCompleted(RakNet::DynDnsResultCode _result, const char *_resultDescription) {Stop(); result=_result; resultDescription=_resultDescription;}
|
||||
|
||||
enum ConnectPhase
|
||||
{
|
||||
CP_CONNECTING_TO_CHECKIP,
|
||||
CP_WAITING_FOR_CHECKIP_RESPONSE,
|
||||
CP_CONNECTING_TO_DYNDNS,
|
||||
CP_WAITING_FOR_DYNDNS_RESPONSE,
|
||||
CP_IDLE,
|
||||
};
|
||||
|
||||
TCPInterface *tcp;
|
||||
RakNet::RakString getString;
|
||||
SystemAddress serverAddress;
|
||||
ConnectPhase connectPhase;
|
||||
RakNet::RakString host;
|
||||
RakNet::Time phaseTimeout;
|
||||
SystemAddress checkIpAddress;
|
||||
const char *resultDescription;
|
||||
RakNet::DynDnsResultCode result;
|
||||
char myIPStr[32];
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif // __DYN_DNS_H
|
||||
|
||||
#endif // _RAKNET_SUPPORT_DynDNS
|
||||
362
third-party/RakNet/src/RakNet/EmailSender.cpp
vendored
Normal file
362
third-party/RakNet/src/RakNet/EmailSender.cpp
vendored
Normal file
@@ -0,0 +1,362 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
// Useful sites
|
||||
// http://www.faqs.org\rfcs\rfc2821.html
|
||||
// http://www2.rad.com\networks/1995/mime/examples.htm
|
||||
|
||||
#include "EmailSender.hpp"
|
||||
#include "TCPInterface.hpp"
|
||||
#include "GetTime.hpp"
|
||||
#include "Rand.hpp"
|
||||
#include "FileList.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#include "RakSleep.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(EmailSender,EmailSender);
|
||||
|
||||
const char *EmailSender::Send(const char *hostAddress, unsigned short hostPort, const char *sender, const char *recipient, const char *senderName, const char *recipientName, const char *subject, const char *body, FileList *attachedFiles, bool doPrintf, const char *password)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
char query[1024];
|
||||
TCPInterface tcpInterface;
|
||||
SystemAddress emailServer;
|
||||
if (tcpInterface.Start(0, 0)==false)
|
||||
return "Unknown error starting TCP";
|
||||
emailServer=tcpInterface.Connect(hostAddress, hostPort,true);
|
||||
if (emailServer==UNASSIGNED_SYSTEM_ADDRESS)
|
||||
return "Failed to connect to host";
|
||||
#if OPEN_SSL_CLIENT_SUPPORT==1
|
||||
tcpInterface.StartSSLClient(emailServer);
|
||||
#endif
|
||||
RakNet::TimeMS timeoutTime = RakNet::GetTimeMS()+3000;
|
||||
packet=0;
|
||||
while (RakNet::GetTimeMS() < timeoutTime)
|
||||
{
|
||||
packet = tcpInterface.Receive();
|
||||
if (packet)
|
||||
{
|
||||
if (doPrintf)
|
||||
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
||||
break;
|
||||
}
|
||||
RakSleep(250);
|
||||
}
|
||||
|
||||
if (packet==0)
|
||||
return "Timeout while waiting for initial data from server.";
|
||||
|
||||
tcpInterface.Send("EHLO\r\n", 6, emailServer,false);
|
||||
const char *response;
|
||||
bool authenticate=false;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4127) // conditional expression is constant
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
|
||||
if (response!=0 && strcmp(response, "AUTHENTICATE")==0)
|
||||
{
|
||||
authenticate=true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Something other than continue?
|
||||
if (response!=0 && strcmp(response, "CONTINUE")!=0)
|
||||
return response;
|
||||
|
||||
// Success?
|
||||
if (response==0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (authenticate)
|
||||
{
|
||||
sprintf(query, "EHLO %s\r\n", sender);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
if (password==0)
|
||||
return "Password needed";
|
||||
char *outputData = RakNet::OP_NEW_ARRAY<char >((const int) (strlen(sender)+strlen(password)+2)*3, _FILE_AND_LINE_ );
|
||||
RakNet::BitStream bs;
|
||||
char zero=0;
|
||||
bs.Write(&zero,1);
|
||||
bs.Write(sender,(const unsigned int)strlen(sender));
|
||||
//bs.Write("jms1@jms1.net",(const unsigned int)strlen("jms1@jms1.net"));
|
||||
bs.Write(&zero,1);
|
||||
bs.Write(password,(const unsigned int)strlen(password));
|
||||
bs.Write(&zero,1);
|
||||
//bs.Write("not.my.real.password",(const unsigned int)strlen("not.my.real.password"));
|
||||
TCPInterface::Base64Encoding((const char*)bs.GetData(), bs.GetNumberOfBytesUsed(), outputData);
|
||||
sprintf(query, "AUTH PLAIN %s", outputData);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
if (sender)
|
||||
sprintf(query, "MAIL From: <%s>\r\n", sender);
|
||||
else
|
||||
sprintf(query, "MAIL From: <>\r\n");
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
|
||||
if (recipient)
|
||||
sprintf(query, "RCPT TO: <%s>\r\n", recipient);
|
||||
else
|
||||
sprintf(query, "RCPT TO: <>\r\n");
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
|
||||
tcpInterface.Send("DATA\r\n", (unsigned int)strlen("DATA\r\n"), emailServer,false);
|
||||
|
||||
// Wait for 354...
|
||||
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
|
||||
if (subject)
|
||||
{
|
||||
sprintf(query, "Subject: %s\r\n", subject);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
}
|
||||
if (senderName)
|
||||
{
|
||||
sprintf(query, "From: %s\r\n", senderName);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
}
|
||||
if (recipientName)
|
||||
{
|
||||
sprintf(query, "To: %s\r\n", recipientName);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
}
|
||||
|
||||
const int boundarySize=60;
|
||||
char boundary[boundarySize+1];
|
||||
int i,j;
|
||||
if (attachedFiles && attachedFiles->fileList.Size())
|
||||
{
|
||||
rakNetRandom.SeedMT((unsigned int) RakNet::GetTimeMS());
|
||||
// Random multipart message boundary
|
||||
for (i=0; i < boundarySize; i++)
|
||||
boundary[i]=TCPInterface::Base64Map()[rakNetRandom.RandomMT()%64];
|
||||
boundary[boundarySize]=0;
|
||||
}
|
||||
|
||||
sprintf(query, "MIME-version: 1.0\r\n");
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
|
||||
if (attachedFiles && attachedFiles->fileList.Size())
|
||||
{
|
||||
sprintf(query, "Content-type: multipart/mixed; BOUNDARY=\"%s\"\r\n\r\n", boundary);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
|
||||
sprintf(query, "This is a multi-part message in MIME format.\r\n\r\n--%s\r\n", boundary);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
}
|
||||
|
||||
sprintf(query, "Content-Type: text/plain; charset=\"US-ASCII\"\r\n\r\n");
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
|
||||
// Write the body of the email, doing some lame shitty shit where I have to make periods at the start of a newline have a second period.
|
||||
char *newBody;
|
||||
int bodyLength;
|
||||
bodyLength=(int)strlen(body);
|
||||
newBody = (char*) rakMalloc_Ex( bodyLength*3, _FILE_AND_LINE_ );
|
||||
if (bodyLength>0)
|
||||
newBody[0]=body[0];
|
||||
for (i=1, j=1; i < bodyLength; i++)
|
||||
{
|
||||
// Transform \n . \r \n into \n . . \r \n
|
||||
if (i < bodyLength-2 &&
|
||||
body[i-1]=='\n' &&
|
||||
body[i+0]=='.' &&
|
||||
body[i+1]=='\r' &&
|
||||
body[i+2]=='\n')
|
||||
{
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='\r';
|
||||
newBody[j++]='\n';
|
||||
i+=2;
|
||||
}
|
||||
// Transform \n . . \r \n into \n . . . \r \n
|
||||
// Having to process .. is a bug in the mail server - the spec says ONLY \r\n.\r\n should be transformed
|
||||
else if (i <= bodyLength-3 &&
|
||||
body[i-1]=='\n' &&
|
||||
body[i+0]=='.' &&
|
||||
body[i+1]=='.' &&
|
||||
body[i+2]=='\r' &&
|
||||
body[i+3]=='\n')
|
||||
{
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='\r';
|
||||
newBody[j++]='\n';
|
||||
i+=3;
|
||||
}
|
||||
// Transform \n . \n into \n . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
|
||||
else if (i < bodyLength-1 &&
|
||||
body[i-1]=='\n' &&
|
||||
body[i+0]=='.' &&
|
||||
body[i+1]=='\n')
|
||||
{
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='\r';
|
||||
newBody[j++]='\n';
|
||||
i+=1;
|
||||
}
|
||||
// Transform \n . . \n into \n . . . \r \n (this is a bug in the mail server - the spec says do not count \n alone but it does)
|
||||
// In fact having to process .. is a bug too - because the spec says ONLY \r\n.\r\n should be transformed
|
||||
else if (i <= bodyLength-2 &&
|
||||
body[i-1]=='\n' &&
|
||||
body[i+0]=='.' &&
|
||||
body[i+1]=='.' &&
|
||||
body[i+2]=='\n')
|
||||
{
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='.';
|
||||
newBody[j++]='\r';
|
||||
newBody[j++]='\n';
|
||||
i+=2;
|
||||
}
|
||||
else
|
||||
newBody[j++]=body[i];
|
||||
}
|
||||
|
||||
newBody[j++]='\r';
|
||||
newBody[j++]='\n';
|
||||
tcpInterface.Send(newBody, j, emailServer,false);
|
||||
|
||||
rakFree_Ex(newBody, _FILE_AND_LINE_ );
|
||||
int outputOffset;
|
||||
|
||||
// What a pain in the rear. I have to map the binary to printable characters using 6 bits per character.
|
||||
if (attachedFiles && attachedFiles->fileList.Size())
|
||||
{
|
||||
for (i=0; i < (int) attachedFiles->fileList.Size(); i++)
|
||||
{
|
||||
// Write boundary
|
||||
sprintf(query, "\r\n--%s\r\n", boundary);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
|
||||
sprintf(query, "Content-Type: APPLICATION/Octet-Stream; SizeOnDisk=%i; name=\"%s\"\r\nContent-Transfer-Encoding: BASE64\r\nContent-Description: %s\r\n\r\n", attachedFiles->fileList[i].dataLengthBytes, attachedFiles->fileList[i].filename.C_String(), attachedFiles->fileList[i].filename.C_String());
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
|
||||
newBody = (char*) rakMalloc_Ex( (size_t) (attachedFiles->fileList[i].dataLengthBytes*3)/2, _FILE_AND_LINE_ );
|
||||
|
||||
outputOffset=TCPInterface::Base64Encoding(attachedFiles->fileList[i].data, (int) attachedFiles->fileList[i].dataLengthBytes, newBody);
|
||||
|
||||
// Send the base64 mapped file.
|
||||
tcpInterface.Send(newBody, outputOffset, emailServer,false);
|
||||
rakFree_Ex(newBody, _FILE_AND_LINE_ );
|
||||
|
||||
}
|
||||
|
||||
// Write last boundary
|
||||
sprintf(query, "\r\n--%s--\r\n", boundary);
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
}
|
||||
|
||||
|
||||
sprintf(query, "\r\n.\r\n");
|
||||
tcpInterface.Send(query, (unsigned int)strlen(query), emailServer,false);
|
||||
response=GetResponse(&tcpInterface, emailServer, doPrintf);
|
||||
if (response!=0)
|
||||
return response;
|
||||
|
||||
tcpInterface.Send("QUIT\r\n", (unsigned int)strlen("QUIT\r\n"), emailServer,false);
|
||||
|
||||
RakSleep(30);
|
||||
if (doPrintf)
|
||||
{
|
||||
packet = tcpInterface.Receive();
|
||||
while (packet)
|
||||
{
|
||||
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
||||
packet = tcpInterface.Receive();
|
||||
}
|
||||
}
|
||||
tcpInterface.Stop();
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
const char *EmailSender::GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf)
|
||||
{
|
||||
RakNet::Packet *packet;
|
||||
RakNet::TimeMS timeout;
|
||||
timeout=RakNet::GetTimeMS()+5000;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4127 ) // warning C4127: conditional expression is constant
|
||||
#endif
|
||||
while (1)
|
||||
{
|
||||
if (tcpInterface->HasLostConnection()==emailServer)
|
||||
return "Connection to server lost.";
|
||||
packet = tcpInterface->Receive();
|
||||
if (packet)
|
||||
{
|
||||
if (doPrintf)
|
||||
{
|
||||
RAKNET_DEBUG_PRINTF("%s", packet->data);
|
||||
}
|
||||
#if OPEN_SSL_CLIENT_SUPPORT==1
|
||||
if (strstr((const char*)packet->data, "220"))
|
||||
{
|
||||
tcpInterface->StartSSLClient(packet->systemAddress);
|
||||
return "AUTHENTICATE"; // OK
|
||||
}
|
||||
// if (strstr((const char*)packet->data, "250-AUTH LOGIN PLAIN"))
|
||||
// {
|
||||
// tcpInterface->StartSSLClient(packet->systemAddress);
|
||||
// return "AUTHENTICATE"; // OK
|
||||
// }
|
||||
#endif
|
||||
if (strstr((const char*)packet->data, "235"))
|
||||
return 0; // Authentication accepted
|
||||
if (strstr((const char*)packet->data, "354"))
|
||||
return 0; // Go ahead
|
||||
#if OPEN_SSL_CLIENT_SUPPORT==1
|
||||
if (strstr((const char*)packet->data, "250-STARTTLS"))
|
||||
{
|
||||
tcpInterface->Send("STARTTLS\r\n", (unsigned int) strlen("STARTTLS\r\n"), packet->systemAddress, false);
|
||||
return "CONTINUE";
|
||||
}
|
||||
#endif
|
||||
if (strstr((const char*)packet->data, "250"))
|
||||
return 0; // OK
|
||||
if (strstr((const char*)packet->data, "550"))
|
||||
return "Failed on error code 550";
|
||||
if (strstr((const char*)packet->data, "553"))
|
||||
return "Failed on error code 553";
|
||||
}
|
||||
if (RakNet::GetTimeMS() > timeout)
|
||||
return "Timed out";
|
||||
RakSleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
58
third-party/RakNet/src/RakNet/EmailSender.hpp
vendored
Normal file
58
third-party/RakNet/src/RakNet/EmailSender.hpp
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/// \file EmailSender.h
|
||||
/// \brief Rudimentary class to send email from code. Don't expect anything fancy.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_EmailSender==1 && _RAKNET_SUPPORT_TCPInterface==1 && _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#ifndef __EMAIL_SENDER_H
|
||||
#define __EMAIL_SENDER_H
|
||||
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "Rand.hpp"
|
||||
#include "TCPInterface.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class FileList;
|
||||
class TCPInterface;
|
||||
|
||||
/// \brief Rudimentary class to send email from code.
|
||||
class RAK_DLL_EXPORT EmailSender
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(EmailSender)
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
protected:
|
||||
const char *GetResponse(TCPInterface *tcpInterface, const SystemAddress &emailServer, bool doPrintf);
|
||||
RakNetRandom rakNetRandom;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
1
third-party/RakNet/src/RakNet/EncodeClassName.cpp
vendored
Normal file
1
third-party/RakNet/src/RakNet/EncodeClassName.cpp
vendored
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
35
third-party/RakNet/src/RakNet/EpochTimeToString.cpp
vendored
Normal file
35
third-party/RakNet/src/RakNet/EpochTimeToString.cpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
#include "FormatString.hpp"
|
||||
#include "EpochTimeToString.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
// localtime
|
||||
#include <time.h>
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
char * EpochTimeToString(long long time)
|
||||
{
|
||||
static int textIndex=0;
|
||||
static char text[4][64];
|
||||
|
||||
if (++textIndex==4)
|
||||
textIndex=0;
|
||||
|
||||
struct tm * timeinfo;
|
||||
time_t t = time;
|
||||
timeinfo = localtime ( &t );
|
||||
strftime (text[textIndex],64,"%c.",timeinfo);
|
||||
|
||||
/*
|
||||
time_t
|
||||
// Copied from the docs
|
||||
struct tm *newtime;
|
||||
newtime = _localtime64(& time);
|
||||
asctime_s( text[textIndex], sizeof(text[textIndex]), newtime );
|
||||
|
||||
while (text[textIndex][0] && (text[textIndex][strlen(text[textIndex])-1]=='\n' || text[textIndex][strlen(text[textIndex])-1]=='\r'))
|
||||
text[textIndex][strlen(text[textIndex])-1]=0;
|
||||
*/
|
||||
|
||||
return text[textIndex];
|
||||
}
|
||||
15
third-party/RakNet/src/RakNet/EpochTimeToString.hpp
vendored
Normal file
15
third-party/RakNet/src/RakNet/EpochTimeToString.hpp
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
/// \file EpochTimeToString.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __EPOCH_TIME_TO_STRING_H
|
||||
#define __EPOCH_TIME_TO_STRING_H
|
||||
|
||||
#include "Export.hpp"
|
||||
|
||||
RAK_DLL_EXPORT char * EpochTimeToString(long long time);
|
||||
|
||||
#endif
|
||||
|
||||
17
third-party/RakNet/src/RakNet/Export.hpp
vendored
Normal file
17
third-party/RakNet/src/RakNet/Export.hpp
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
// BEGIN Aya CODE
|
||||
#pragma once
|
||||
// END Aya CODE
|
||||
|
||||
#include "RakNetDefines.hpp"
|
||||
|
||||
#if defined(_WIN32) && !(defined(__GNUC__) || defined(__GCCXML__)) && !defined(_RAKNET_LIB) && defined(_RAKNET_DLL)
|
||||
#define RAK_DLL_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define RAK_DLL_EXPORT
|
||||
#endif
|
||||
|
||||
#define STATIC_FACTORY_DECLARATIONS(x) static x* GetInstance(void); \
|
||||
static void DestroyInstance( x *i);
|
||||
|
||||
#define STATIC_FACTORY_DEFINITIONS(x,y) x* x::GetInstance(void) {return RakNet::OP_NEW<y>( _FILE_AND_LINE_ );} \
|
||||
void x::DestroyInstance( x *i) {RakNet::OP_DELETE(( y* ) i, _FILE_AND_LINE_);}
|
||||
798
third-party/RakNet/src/RakNet/FileList.cpp
vendored
Normal file
798
third-party/RakNet/src/RakNet/FileList.cpp
vendored
Normal file
@@ -0,0 +1,798 @@
|
||||
#include "FileList.hpp"
|
||||
|
||||
#if _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#include <stdio.h> // RAKNET_DEBUG_PRINTF
|
||||
#include "RakAssert.hpp"
|
||||
#if defined(ANDROID)
|
||||
#include <asm/io.h>
|
||||
#elif defined(_WIN32) || defined(__CYGWIN__)
|
||||
#include <io.h>
|
||||
|
||||
|
||||
#elif !defined ( __APPLE__ ) && !defined ( __APPLE_CC__ ) && !defined ( __PPC__ ) && !defined ( __FreeBSD__ ) && !defined ( __S3E__ ) && !defined(__aarch64__)
|
||||
#include <sys/io.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// For mkdir
|
||||
#include <direct.h>
|
||||
|
||||
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
//#include "SHA1.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "StringCompressor.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "FileOperations.hpp"
|
||||
#include "SuperFastHash.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
#define MAX_FILENAME_LENGTH 512
|
||||
static const unsigned HASH_LENGTH=4;
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
// alloca
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <malloc.h>
|
||||
|
||||
|
||||
#else
|
||||
#if !defined ( __FreeBSD__ )
|
||||
#include <alloca.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include "_FindFirst.hpp"
|
||||
#include <stdint.h> //defines intptr_t
|
||||
#endif
|
||||
|
||||
#include "RakAlloca.hpp"
|
||||
|
||||
//int RAK_DLL_EXPORT FileListNodeComp( char * const &key, const FileListNode &data )
|
||||
//{
|
||||
// return strcmp(key, data.filename);
|
||||
//}
|
||||
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(FileListProgress,FileListProgress)
|
||||
STATIC_FACTORY_DEFINITIONS(FLP_Printf,FLP_Printf)
|
||||
STATIC_FACTORY_DEFINITIONS(FileList,FileList)
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
/// First callback called when FileList::AddFilesFromDirectory() starts
|
||||
void FLP_Printf::OnAddFilesFromDirectoryStarted(FileList *fileList, char *dir) {
|
||||
(void) fileList;
|
||||
RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dir);}
|
||||
|
||||
/// Called for each directory, when that directory begins processing
|
||||
void FLP_Printf::OnDirectory(FileList *fileList, char *dir, unsigned int directoriesRemaining) {
|
||||
(void) fileList;
|
||||
RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", dir, directoriesRemaining);}
|
||||
void FLP_Printf::OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID )
|
||||
{
|
||||
(void) setID;
|
||||
|
||||
char str[32];
|
||||
systemAddress.ToString(true, (char*) str);
|
||||
RAKNET_DEBUG_PRINTF("File pushes complete to %s\n", str);
|
||||
}
|
||||
void FLP_Printf::OnSendAborted( SystemAddress systemAddress )
|
||||
{
|
||||
char str[32];
|
||||
systemAddress.ToString(true, (char*) str);
|
||||
RAKNET_DEBUG_PRINTF("Send aborted to %s\n", str);
|
||||
}
|
||||
FileList::FileList()
|
||||
{
|
||||
}
|
||||
FileList::~FileList()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
void FileList::AddFile(const char *filepath, const char *filename, FileListNodeContext context)
|
||||
{
|
||||
if (filepath==0 || filename==0)
|
||||
return;
|
||||
|
||||
char *data;
|
||||
//std::fstream file;
|
||||
//file.open(filename, std::ios::in | std::ios::binary);
|
||||
|
||||
FILE *fp = fopen(filepath, "rb");
|
||||
if (fp==0)
|
||||
return;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
int length = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
if (length > (int) ((unsigned int)-1 / 8))
|
||||
{
|
||||
// If this assert hits, split up your file. You could also change BitSize_t in RakNetTypes.h to unsigned long long but this is not recommended for performance reasons
|
||||
RakAssert("Cannot add files over 536 MB" && 0);
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool usedAlloca=false;
|
||||
if (length < MAX_ALLOCA_STACK_ALLOCATION)
|
||||
{
|
||||
data = ( char* ) alloca( length );
|
||||
usedAlloca=true;
|
||||
}
|
||||
else
|
||||
|
||||
{
|
||||
data = (char*) rakMalloc_Ex( length, _FILE_AND_LINE_ );
|
||||
}
|
||||
|
||||
fread(data, 1, length, fp);
|
||||
AddFile(filename, filepath, data, length, length, context);
|
||||
fclose(fp);
|
||||
|
||||
|
||||
if (usedAlloca==false)
|
||||
|
||||
rakFree_Ex(data, _FILE_AND_LINE_ );
|
||||
|
||||
}
|
||||
void FileList::AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference, bool takeDataPointer)
|
||||
{
|
||||
if (filename==0)
|
||||
return;
|
||||
if (strlen(filename)>MAX_FILENAME_LENGTH)
|
||||
{
|
||||
// Should be enough for anyone
|
||||
RakAssert(0);
|
||||
return;
|
||||
}
|
||||
// If adding a reference, do not send data
|
||||
RakAssert(isAReference==false || data==0);
|
||||
// Avoid duplicate insertions unless the data is different, in which case overwrite the old data
|
||||
unsigned i;
|
||||
for (i=0; i<fileList.Size();i++)
|
||||
{
|
||||
if (strcmp(fileList[i].filename, filename)==0)
|
||||
{
|
||||
if (fileList[i].fileLengthBytes==fileLength && fileList[i].dataLengthBytes==dataLength &&
|
||||
(dataLength==0 || fileList[i].data==0 ||
|
||||
memcmp(fileList[i].data, data, dataLength)==0
|
||||
))
|
||||
// Exact same file already here
|
||||
return;
|
||||
|
||||
// File of the same name, but different contents, so overwrite
|
||||
rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
|
||||
fileList.RemoveAtIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FileListNode n;
|
||||
// size_t fileNameLen = strlen(filename);
|
||||
if (dataLength && data)
|
||||
{
|
||||
if (takeDataPointer)
|
||||
{
|
||||
n.data=(char*) data;
|
||||
}
|
||||
else
|
||||
{
|
||||
n.data=(char*) rakMalloc_Ex( dataLength, _FILE_AND_LINE_ );
|
||||
memcpy(n.data, data, dataLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
n.data=0;
|
||||
n.dataLengthBytes=dataLength;
|
||||
n.fileLengthBytes=fileLength;
|
||||
n.isAReference=isAReference;
|
||||
n.context=context;
|
||||
if (n.context.dataPtr==0)
|
||||
n.context.dataPtr=n.data;
|
||||
if (n.context.dataLength==0)
|
||||
n.context.dataLength=dataLength;
|
||||
n.filename=filename;
|
||||
n.fullPathToFile=fullPathToFile;
|
||||
|
||||
fileList.Insert(n, _FILE_AND_LINE_);
|
||||
}
|
||||
void FileList::AddFilesFromDirectory(const char *applicationDirectory, const char *subDirectory, bool writeHash, bool writeData, bool recursive, FileListNodeContext context)
|
||||
{
|
||||
|
||||
|
||||
|
||||
DataStructures::Queue<char*> dirList;
|
||||
char root[260];
|
||||
char fullPath[520];
|
||||
_finddata_t fileInfo;
|
||||
intptr_t dir;
|
||||
FILE *fp;
|
||||
char *dirSoFar, *fileData;
|
||||
dirSoFar=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ );
|
||||
|
||||
if (applicationDirectory)
|
||||
strcpy(root, applicationDirectory);
|
||||
else
|
||||
root[0]=0;
|
||||
|
||||
int rootLen=(int)strlen(root);
|
||||
if (rootLen)
|
||||
{
|
||||
strcpy(dirSoFar, root);
|
||||
if (FixEndingSlash(dirSoFar))
|
||||
rootLen++;
|
||||
}
|
||||
else
|
||||
dirSoFar[0]=0;
|
||||
|
||||
if (subDirectory)
|
||||
{
|
||||
strcat(dirSoFar, subDirectory);
|
||||
FixEndingSlash(dirSoFar);
|
||||
}
|
||||
for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
|
||||
fileListProgressCallbacks[flpcIndex]->OnAddFilesFromDirectoryStarted(this, dirSoFar);
|
||||
// RAKNET_DEBUG_PRINTF("Adding files from directory %s\n",dirSoFar);
|
||||
dirList.Push(dirSoFar, _FILE_AND_LINE_ );
|
||||
while (dirList.Size())
|
||||
{
|
||||
dirSoFar=dirList.Pop();
|
||||
strcpy(fullPath, dirSoFar);
|
||||
// Changed from *.* to * for Linux compatibility
|
||||
strcat(fullPath, "*");
|
||||
|
||||
|
||||
dir=_findfirst(fullPath, &fileInfo );
|
||||
if (dir==-1)
|
||||
{
|
||||
_findclose(dir);
|
||||
rakFree_Ex(dirSoFar, _FILE_AND_LINE_ );
|
||||
unsigned i;
|
||||
for (i=0; i < dirList.Size(); i++)
|
||||
rakFree_Ex(dirList[i], _FILE_AND_LINE_ );
|
||||
return;
|
||||
}
|
||||
|
||||
// RAKNET_DEBUG_PRINTF("Adding %s. %i remaining.\n", fullPath, dirList.Size());
|
||||
for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
|
||||
fileListProgressCallbacks[flpcIndex]->OnDirectory(this, fullPath, dirList.Size());
|
||||
|
||||
do
|
||||
{
|
||||
// no guarantee these entries are first...
|
||||
if (strcmp("." , fileInfo.name) == 0 ||
|
||||
strcmp("..", fileInfo.name) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((fileInfo.attrib & (_A_HIDDEN | _A_SUBDIR | _A_SYSTEM))==0)
|
||||
{
|
||||
strcpy(fullPath, dirSoFar);
|
||||
strcat(fullPath, fileInfo.name);
|
||||
fileData=0;
|
||||
|
||||
for (unsigned int flpcIndex=0; flpcIndex < fileListProgressCallbacks.Size(); flpcIndex++)
|
||||
fileListProgressCallbacks[flpcIndex]->OnFile(this, dirSoFar, fileInfo.name, fileInfo.size);
|
||||
|
||||
if (writeData && writeHash)
|
||||
{
|
||||
fp = fopen(fullPath, "rb");
|
||||
if (fp)
|
||||
{
|
||||
fileData= (char*) rakMalloc_Ex( fileInfo.size+HASH_LENGTH, _FILE_AND_LINE_ );
|
||||
fread(fileData+HASH_LENGTH, fileInfo.size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
unsigned int hash = SuperFastHash(fileData+HASH_LENGTH, fileInfo.size);
|
||||
if (RakNet::BitStream::DoEndianSwap())
|
||||
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
||||
memcpy(fileData, &hash, HASH_LENGTH);
|
||||
|
||||
// sha1.Reset();
|
||||
// sha1.Update( ( unsigned char* ) fileData+HASH_LENGTH, fileInfo.size );
|
||||
// sha1.Final();
|
||||
// memcpy(fileData, sha1.GetHash(), HASH_LENGTH);
|
||||
// File data and hash
|
||||
AddFile((const char*)fullPath+rootLen, fullPath, fileData, fileInfo.size+HASH_LENGTH, fileInfo.size, context);
|
||||
}
|
||||
}
|
||||
else if (writeHash)
|
||||
{
|
||||
// sha1.Reset();
|
||||
// sha1.HashFile((char*)fullPath);
|
||||
// sha1.Final();
|
||||
|
||||
unsigned int hash = SuperFastHashFile(fullPath);
|
||||
if (RakNet::BitStream::DoEndianSwap())
|
||||
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
||||
|
||||
// Hash only
|
||||
// AddFile((const char*)fullPath+rootLen, (const char*)sha1.GetHash(), HASH_LENGTH, fileInfo.size, context);
|
||||
AddFile((const char*)fullPath+rootLen, fullPath, (const char*)&hash, HASH_LENGTH, fileInfo.size, context);
|
||||
}
|
||||
else if (writeData)
|
||||
{
|
||||
fileData= (char*) rakMalloc_Ex( fileInfo.size, _FILE_AND_LINE_ );
|
||||
fp = fopen(fullPath, "rb");
|
||||
fread(fileData, fileInfo.size, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
// File data only
|
||||
AddFile(fullPath+rootLen, fullPath, fileData, fileInfo.size, fileInfo.size, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Just the filename
|
||||
AddFile(fullPath+rootLen, fullPath, 0, 0, fileInfo.size, context);
|
||||
}
|
||||
|
||||
if (fileData)
|
||||
rakFree_Ex(fileData, _FILE_AND_LINE_ );
|
||||
}
|
||||
else if ((fileInfo.attrib & _A_SUBDIR) && (fileInfo.attrib & (_A_HIDDEN | _A_SYSTEM))==0 && recursive)
|
||||
{
|
||||
char *newDir=(char*) rakMalloc_Ex( 520, _FILE_AND_LINE_ );
|
||||
strcpy(newDir, dirSoFar);
|
||||
strcat(newDir, fileInfo.name);
|
||||
strcat(newDir, "/");
|
||||
dirList.Push(newDir, _FILE_AND_LINE_ );
|
||||
}
|
||||
|
||||
} while (_findnext(dir, &fileInfo ) != -1);
|
||||
|
||||
_findclose(dir);
|
||||
rakFree_Ex(dirSoFar, _FILE_AND_LINE_ );
|
||||
}
|
||||
|
||||
}
|
||||
void FileList::Clear(void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i<fileList.Size(); i++)
|
||||
{
|
||||
rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
|
||||
}
|
||||
fileList.Clear(false, _FILE_AND_LINE_);
|
||||
}
|
||||
void FileList::Serialize(RakNet::BitStream *outBitStream)
|
||||
{
|
||||
outBitStream->WriteCompressed(fileList.Size());
|
||||
unsigned i;
|
||||
for (i=0; i < fileList.Size(); i++)
|
||||
{
|
||||
outBitStream->WriteCompressed(fileList[i].context.op);
|
||||
outBitStream->WriteCompressed(fileList[i].context.fileId);
|
||||
StringCompressor::Instance()->EncodeString(fileList[i].filename.C_String(), MAX_FILENAME_LENGTH, outBitStream);
|
||||
|
||||
bool writeFileData = fileList[i].dataLengthBytes>0==true;
|
||||
outBitStream->Write(writeFileData);
|
||||
if (writeFileData)
|
||||
{
|
||||
outBitStream->WriteCompressed(fileList[i].dataLengthBytes);
|
||||
outBitStream->Write(fileList[i].data, fileList[i].dataLengthBytes);
|
||||
}
|
||||
|
||||
outBitStream->Write((bool)(fileList[i].fileLengthBytes==fileList[i].dataLengthBytes));
|
||||
if (fileList[i].fileLengthBytes!=fileList[i].dataLengthBytes)
|
||||
outBitStream->WriteCompressed(fileList[i].fileLengthBytes);
|
||||
}
|
||||
}
|
||||
bool FileList::Deserialize(RakNet::BitStream *inBitStream)
|
||||
{
|
||||
bool b, dataLenNonZero=false, fileLenMatchesDataLen=false;
|
||||
char filename[512];
|
||||
unsigned int fileListSize;
|
||||
FileListNode n;
|
||||
b=inBitStream->ReadCompressed(fileListSize);
|
||||
#ifdef _DEBUG
|
||||
RakAssert(b);
|
||||
RakAssert(fileListSize < 10000);
|
||||
#endif
|
||||
if (b==false || fileListSize > 10000)
|
||||
return false; // Sanity check
|
||||
Clear();
|
||||
unsigned i;
|
||||
for (i=0; i < fileListSize; i++)
|
||||
{
|
||||
inBitStream->ReadCompressed(n.context.op);
|
||||
inBitStream->ReadCompressed(n.context.fileId);
|
||||
StringCompressor::Instance()->DecodeString((char*)filename, MAX_FILENAME_LENGTH, inBitStream);
|
||||
inBitStream->Read(dataLenNonZero);
|
||||
if (dataLenNonZero)
|
||||
{
|
||||
inBitStream->ReadCompressed(n.dataLengthBytes);
|
||||
// sanity check
|
||||
if (n.dataLengthBytes>2000000000)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(n.dataLengthBytes<=2000000000);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
n.data=(char*) rakMalloc_Ex( (size_t) n.dataLengthBytes, _FILE_AND_LINE_ );
|
||||
inBitStream->Read(n.data, n.dataLengthBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
n.dataLengthBytes=0;
|
||||
n.data=0;
|
||||
}
|
||||
|
||||
b=inBitStream->Read(fileLenMatchesDataLen);
|
||||
if (fileLenMatchesDataLen)
|
||||
n.fileLengthBytes=(unsigned) n.dataLengthBytes;
|
||||
else
|
||||
b=inBitStream->ReadCompressed(n.fileLengthBytes);
|
||||
#ifdef _DEBUG
|
||||
RakAssert(b);
|
||||
#endif
|
||||
if (b==0)
|
||||
{
|
||||
Clear();
|
||||
return false;
|
||||
}
|
||||
n.filename=filename;
|
||||
n.fullPathToFile=filename;
|
||||
fileList.Insert(n, _FILE_AND_LINE_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
void FileList::GetDeltaToCurrent(FileList *input, FileList *output, const char *dirSubset, const char *remoteSubdir)
|
||||
{
|
||||
// For all files in this list that do not match the input list, write them to the output list.
|
||||
// dirSubset allows checking only a portion of the files in this list.
|
||||
unsigned thisIndex, inputIndex;
|
||||
unsigned dirSubsetLen, localPathLen, remoteSubdirLen;
|
||||
bool match;
|
||||
if (dirSubset)
|
||||
dirSubsetLen = (unsigned int) strlen(dirSubset);
|
||||
else
|
||||
dirSubsetLen = 0;
|
||||
if (remoteSubdir && remoteSubdir[0])
|
||||
{
|
||||
remoteSubdirLen=(unsigned int) strlen(remoteSubdir);
|
||||
if (IsSlash(remoteSubdir[remoteSubdirLen-1]))
|
||||
remoteSubdirLen--;
|
||||
}
|
||||
else
|
||||
remoteSubdirLen=0;
|
||||
|
||||
for (thisIndex=0; thisIndex < fileList.Size(); thisIndex++)
|
||||
{
|
||||
localPathLen = (unsigned int) fileList[thisIndex].filename.GetLength();
|
||||
while (localPathLen>0)
|
||||
{
|
||||
if (IsSlash(fileList[thisIndex].filename[localPathLen-1]))
|
||||
{
|
||||
localPathLen--;
|
||||
break;
|
||||
}
|
||||
localPathLen--;
|
||||
}
|
||||
|
||||
// fileList[thisIndex].filename has to match dirSubset and be shorter or equal to it in length.
|
||||
if (dirSubsetLen>0 &&
|
||||
(localPathLen<dirSubsetLen ||
|
||||
_strnicmp(fileList[thisIndex].filename.C_String(), dirSubset, dirSubsetLen)!=0 ||
|
||||
(localPathLen>dirSubsetLen && IsSlash(fileList[thisIndex].filename[dirSubsetLen])==false)))
|
||||
continue;
|
||||
|
||||
match=false;
|
||||
for (inputIndex=0; inputIndex < input->fileList.Size(); inputIndex++)
|
||||
{
|
||||
// If the filenames, hashes, and lengths match then skip this element in fileList. Otherwise write it to output
|
||||
if (_stricmp(input->fileList[inputIndex].filename.C_String()+remoteSubdirLen,fileList[thisIndex].filename.C_String()+dirSubsetLen)==0)
|
||||
{
|
||||
match=true;
|
||||
if (input->fileList[inputIndex].fileLengthBytes==fileList[thisIndex].fileLengthBytes &&
|
||||
input->fileList[inputIndex].dataLengthBytes==fileList[thisIndex].dataLengthBytes &&
|
||||
memcmp(input->fileList[inputIndex].data,fileList[thisIndex].data,(size_t) fileList[thisIndex].dataLengthBytes)==0)
|
||||
{
|
||||
// File exists on both machines and is the same.
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// File exists on both machines and is not the same.
|
||||
output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (match==false)
|
||||
{
|
||||
// Other system does not have the file at all
|
||||
output->AddFile(fileList[thisIndex].filename, fileList[thisIndex].fullPathToFile, 0,0, fileList[thisIndex].fileLengthBytes, FileListNodeContext(0,0), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
void FileList::ListMissingOrChangedFiles(const char *applicationDirectory, FileList *missingOrChangedFiles, bool alwaysWriteHash, bool neverWriteHash)
|
||||
{
|
||||
unsigned fileLength;
|
||||
// CSHA1 sha1;
|
||||
FILE *fp;
|
||||
char fullPath[512];
|
||||
unsigned i;
|
||||
// char *fileData;
|
||||
|
||||
for (i=0; i < fileList.Size(); i++)
|
||||
{
|
||||
strcpy(fullPath, applicationDirectory);
|
||||
FixEndingSlash(fullPath);
|
||||
strcat(fullPath,fileList[i].filename);
|
||||
fp=fopen(fullPath, "rb");
|
||||
if (fp==0)
|
||||
{
|
||||
missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, 0, FileListNodeContext(0,0), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
fileLength = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
if (fileLength != fileList[i].fileLengthBytes && alwaysWriteHash==false)
|
||||
{
|
||||
missingOrChangedFiles->AddFile(fileList[i].filename, fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// fileData= (char*) rakMalloc_Ex( fileLength, _FILE_AND_LINE_ );
|
||||
// fread(fileData, fileLength, 1, fp);
|
||||
|
||||
// sha1.Reset();
|
||||
// sha1.Update( ( unsigned char* ) fileData, fileLength );
|
||||
// sha1.Final();
|
||||
|
||||
// rakFree_Ex(fileData, _FILE_AND_LINE_ );
|
||||
|
||||
unsigned int hash = SuperFastHashFilePtr(fp);
|
||||
if (RakNet::BitStream::DoEndianSwap())
|
||||
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
||||
|
||||
//if (fileLength != fileList[i].fileLength || memcmp( sha1.GetHash(), fileList[i].data, HASH_LENGTH)!=0)
|
||||
if (fileLength != fileList[i].fileLengthBytes || memcmp( &hash, fileList[i].data, HASH_LENGTH)!=0)
|
||||
{
|
||||
if (neverWriteHash==false)
|
||||
// missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)sha1.GetHash(), HASH_LENGTH, fileLength, 0);
|
||||
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, (const char *) &hash, HASH_LENGTH, fileLength, FileListNodeContext(0,0), false);
|
||||
else
|
||||
missingOrChangedFiles->AddFile((const char*)fileList[i].filename, (const char*)fileList[i].fullPathToFile, 0, 0, fileLength, FileListNodeContext(0,0), false);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
}
|
||||
void FileList::PopulateDataFromDisk(const char *applicationDirectory, bool writeFileData, bool writeFileHash, bool removeUnknownFiles)
|
||||
{
|
||||
FILE *fp;
|
||||
char fullPath[512];
|
||||
unsigned i;
|
||||
// CSHA1 sha1;
|
||||
|
||||
i=0;
|
||||
while (i < fileList.Size())
|
||||
{
|
||||
rakFree_Ex(fileList[i].data, _FILE_AND_LINE_ );
|
||||
strcpy(fullPath, applicationDirectory);
|
||||
FixEndingSlash(fullPath);
|
||||
strcat(fullPath,fileList[i].filename.C_String());
|
||||
fp=fopen(fullPath, "rb");
|
||||
if (fp)
|
||||
{
|
||||
if (writeFileHash || writeFileData)
|
||||
{
|
||||
fseek(fp, 0, SEEK_END);
|
||||
fileList[i].fileLengthBytes = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
if (writeFileHash)
|
||||
{
|
||||
if (writeFileData)
|
||||
{
|
||||
// Hash + data so offset the data by HASH_LENGTH
|
||||
fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes+HASH_LENGTH, _FILE_AND_LINE_ );
|
||||
fread(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes, 1, fp);
|
||||
// sha1.Reset();
|
||||
// sha1.Update((unsigned char*)fileList[i].data+HASH_LENGTH, fileList[i].fileLength);
|
||||
// sha1.Final();
|
||||
unsigned int hash = SuperFastHash(fileList[i].data+HASH_LENGTH, fileList[i].fileLengthBytes);
|
||||
if (RakNet::BitStream::DoEndianSwap())
|
||||
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
||||
// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
|
||||
memcpy(fileList[i].data, &hash, HASH_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hash only
|
||||
fileList[i].dataLengthBytes=HASH_LENGTH;
|
||||
if (fileList[i].fileLengthBytes < HASH_LENGTH)
|
||||
fileList[i].data=(char*) rakMalloc_Ex( HASH_LENGTH, _FILE_AND_LINE_ );
|
||||
else
|
||||
fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ );
|
||||
fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
|
||||
// sha1.Reset();
|
||||
// sha1.Update((unsigned char*)fileList[i].data, fileList[i].fileLength);
|
||||
// sha1.Final();
|
||||
unsigned int hash = SuperFastHash(fileList[i].data, fileList[i].fileLengthBytes);
|
||||
if (RakNet::BitStream::DoEndianSwap())
|
||||
RakNet::BitStream::ReverseBytesInPlace((unsigned char*) &hash, sizeof(hash));
|
||||
// memcpy(fileList[i].data, sha1.GetHash(), HASH_LENGTH);
|
||||
memcpy(fileList[i].data, &hash, HASH_LENGTH);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Data only
|
||||
fileList[i].dataLengthBytes=fileList[i].fileLengthBytes;
|
||||
fileList[i].data=(char*) rakMalloc_Ex( fileList[i].fileLengthBytes, _FILE_AND_LINE_ );
|
||||
fread(fileList[i].data, fileList[i].fileLengthBytes, 1, fp);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fileList[i].data=0;
|
||||
fileList[i].dataLengthBytes=0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (removeUnknownFiles)
|
||||
{
|
||||
fileList.RemoveAtIndex(i);
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
void FileList::FlagFilesAsReferences(void)
|
||||
{
|
||||
for (unsigned int i=0; i < fileList.Size(); i++)
|
||||
{
|
||||
fileList[i].isAReference=true;
|
||||
fileList[i].dataLengthBytes=fileList[i].fileLengthBytes;
|
||||
}
|
||||
}
|
||||
void FileList::WriteDataToDisk(const char *applicationDirectory)
|
||||
{
|
||||
char fullPath[512];
|
||||
unsigned i,j;
|
||||
|
||||
for (i=0; i < fileList.Size(); i++)
|
||||
{
|
||||
strcpy(fullPath, applicationDirectory);
|
||||
FixEndingSlash(fullPath);
|
||||
strcat(fullPath,fileList[i].filename.C_String());
|
||||
|
||||
// Security - Don't allow .. in the filename anywhere so you can't write outside of the root directory
|
||||
for (j=1; j < fileList[i].filename.GetLength(); j++)
|
||||
{
|
||||
if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(0);
|
||||
#endif
|
||||
// Just cancel the write entirely
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
WriteFileWithDirectories(fullPath, fileList[i].data, (unsigned int) fileList[i].dataLengthBytes);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4966 ) // unlink declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated.
|
||||
#endif
|
||||
void FileList::DeleteFiles(const char *applicationDirectory)
|
||||
{
|
||||
|
||||
|
||||
|
||||
char fullPath[512];
|
||||
unsigned i,j;
|
||||
|
||||
for (i=0; i < fileList.Size(); i++)
|
||||
{
|
||||
// The filename should not have .. in the path - if it does ignore it
|
||||
for (j=1; j < fileList[i].filename.GetLength(); j++)
|
||||
{
|
||||
if (fileList[i].filename[j]=='.' && fileList[i].filename[j-1]=='.')
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
RakAssert(0);
|
||||
#endif
|
||||
// Just cancel the deletion entirely
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
strcpy(fullPath, applicationDirectory);
|
||||
FixEndingSlash(fullPath);
|
||||
strcat(fullPath, fileList[i].filename.C_String());
|
||||
|
||||
// BEGIN Aya CHANGES
|
||||
#ifdef _WIN32
|
||||
int result = _unlink(fullPath);
|
||||
#else
|
||||
int result = unlink(fullPath);
|
||||
#endif
|
||||
// END Aya CHANGES
|
||||
|
||||
if (result!=0)
|
||||
{
|
||||
RAKNET_DEBUG_PRINTF("FileList::DeleteFiles: unlink (%s) failed.\n", fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FileList::AddCallback(FileListProgress *cb)
|
||||
{
|
||||
if (cb==0)
|
||||
return;
|
||||
|
||||
if ((unsigned int) fileListProgressCallbacks.GetIndexOf(cb)==(unsigned int)-1)
|
||||
fileListProgressCallbacks.Push(cb, _FILE_AND_LINE_);
|
||||
}
|
||||
void FileList::RemoveCallback(FileListProgress *cb)
|
||||
{
|
||||
unsigned int idx = fileListProgressCallbacks.GetIndexOf(cb);
|
||||
if (idx!=(unsigned int) -1)
|
||||
fileListProgressCallbacks.RemoveAtIndex(idx);
|
||||
}
|
||||
void FileList::ClearCallbacks(void)
|
||||
{
|
||||
fileListProgressCallbacks.Clear(true, _FILE_AND_LINE_);
|
||||
}
|
||||
void FileList::GetCallbacks(DataStructures::List<FileListProgress*> &callbacks)
|
||||
{
|
||||
callbacks = fileListProgressCallbacks;
|
||||
}
|
||||
|
||||
|
||||
bool FileList::FixEndingSlash(char *str)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (str[strlen(str)-1]!='/' && str[strlen(str)-1]!='\\')
|
||||
{
|
||||
strcat(str, "\\"); // Only \ works with system commands, used by AutopatcherClient
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
if (str[strlen(str)-1]!='\\' && str[strlen(str)-1]!='/')
|
||||
{
|
||||
strcat(str, "/"); // Only / works with Linux
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_FileOperations
|
||||
258
third-party/RakNet/src/RakNet/FileList.hpp
vendored
Normal file
258
third-party/RakNet/src/RakNet/FileList.hpp
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
/// \file FileList.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#ifndef __FILE_LIST
|
||||
#define __FILE_LIST
|
||||
|
||||
#include "Export.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "FileListNodeContext.hpp"
|
||||
#include "RakString.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
class BitStream;
|
||||
}
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
class FileList;
|
||||
|
||||
|
||||
/// Represents once instance of a file
|
||||
struct FileListNode
|
||||
{
|
||||
/// Name of the file
|
||||
RakNet::RakString filename;
|
||||
|
||||
/// Full path to the file, which may be different than filename
|
||||
RakNet::RakString fullPathToFile;
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
/// Callback interface set with FileList::SetCallback() in case you want progress notifications when FileList::AddFilesFromDirectory() is called
|
||||
class RAK_DLL_EXPORT FileListProgress
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(FileListProgress)
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// \brief 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, unsigned short setId)
|
||||
{
|
||||
(void) fileName;
|
||||
(void) fileLengthBytes;
|
||||
(void) offset;
|
||||
(void) bytesBeingSent;
|
||||
(void) done;
|
||||
(void) targetSystem;
|
||||
(void) setId;
|
||||
}
|
||||
|
||||
/// \brief This function is called when all files have been read and are being transferred to a remote system
|
||||
virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setId )
|
||||
{
|
||||
(void) systemAddress;
|
||||
(void) setId;
|
||||
}
|
||||
|
||||
/// \brief This function is called when a send to a system was aborted (probably due to disconnection)
|
||||
virtual void OnSendAborted( SystemAddress systemAddress )
|
||||
{
|
||||
(void) systemAddress;
|
||||
}
|
||||
};
|
||||
|
||||
/// Implementation of FileListProgress to use RAKNET_DEBUG_PRINTF
|
||||
class RAK_DLL_EXPORT FLP_Printf : public FileListProgress
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(FLP_Printf)
|
||||
|
||||
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);
|
||||
|
||||
/// \brief This function is called when all files have been transferred to a particular remote system
|
||||
virtual void OnFilePushesComplete( SystemAddress systemAddress, unsigned short setID );
|
||||
|
||||
/// \brief This function is called when a send to a system was aborted (probably due to disconnection)
|
||||
virtual void OnSendAborted( SystemAddress systemAddress );
|
||||
};
|
||||
|
||||
class RAK_DLL_EXPORT FileList
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(FileList)
|
||||
|
||||
FileList();
|
||||
~FileList();
|
||||
/// \brief 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 4 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);
|
||||
|
||||
/// \brief Given the existing set of files, search applicationDirectory for the same files.
|
||||
/// \details 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);
|
||||
|
||||
/// \brief Return the files that need to be written to make \a input match this current FileList.
|
||||
/// \details 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);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief Write all files to disk, prefixing the paths with applicationDirectory
|
||||
/// \param[in] applicationDirectory path prefix
|
||||
void WriteDataToDisk(const char *applicationDirectory);
|
||||
|
||||
/// \brief 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] fullPathToFile Full path to the file on disk
|
||||
/// \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
|
||||
/// \param[in] takeDataPointer If true, do not allocate dataLength. Just take the pointer passed to the \a data parameter
|
||||
void AddFile(const char *filename, const char *fullPathToFile, const char *data, const unsigned dataLength, const unsigned fileLength, FileListNodeContext context, bool isAReference=false, bool takeDataPointer=false);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief Adds 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 AddCallback(FileListProgress *cb);
|
||||
|
||||
/// \brief Removes a callback
|
||||
/// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback()
|
||||
void RemoveCallback(FileListProgress *cb);
|
||||
|
||||
/// \brief Removes all callbacks
|
||||
void ClearCallbacks(void);
|
||||
|
||||
/// Returns all callbacks added with AddCallback()
|
||||
/// \param[out] callbacks The list is set to the list of callbacks
|
||||
void GetCallbacks(DataStructures::List<FileListProgress*> &callbacks);
|
||||
|
||||
// Here so you can read it, but don't modify it
|
||||
DataStructures::List<FileListNode> fileList;
|
||||
|
||||
static bool FixEndingSlash(char *str);
|
||||
protected:
|
||||
DataStructures::List<FileListProgress*> fileListProgressCallbacks;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_FileOperations
|
||||
39
third-party/RakNet/src/RakNet/FileListNodeContext.hpp
vendored
Normal file
39
third-party/RakNet/src/RakNet/FileListNodeContext.hpp
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
/// \file FileListNodeContext.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __FILE_LIST_NODE_CONTEXT_H
|
||||
#define __FILE_LIST_NODE_CONTEXT_H
|
||||
|
||||
#include "BitStream.hpp"
|
||||
|
||||
struct FileListNodeContext
|
||||
{
|
||||
FileListNodeContext() {dataPtr=0; dataLength=0;}
|
||||
FileListNodeContext(unsigned char o, unsigned int f) : op(o), fileId(f) {dataPtr=0; dataLength=0;}
|
||||
~FileListNodeContext() {}
|
||||
|
||||
unsigned char op;
|
||||
unsigned int fileId;
|
||||
void *dataPtr;
|
||||
unsigned int dataLength;
|
||||
};
|
||||
|
||||
inline RakNet::BitStream& operator<<(RakNet::BitStream& out, FileListNodeContext& in)
|
||||
{
|
||||
out.Write(in.op);
|
||||
out.Write(in.fileId);
|
||||
return out;
|
||||
}
|
||||
inline RakNet::BitStream& operator>>(RakNet::BitStream& in, FileListNodeContext& out)
|
||||
{
|
||||
in.Read(out.op);
|
||||
bool success = in.Read(out.fileId);
|
||||
(void) success;
|
||||
assert(success);
|
||||
return in;
|
||||
}
|
||||
|
||||
#endif
|
||||
1086
third-party/RakNet/src/RakNet/FileListTransfer.cpp
vendored
Normal file
1086
third-party/RakNet/src/RakNet/FileListTransfer.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
173
third-party/RakNet/src/RakNet/FileListTransfer.hpp
vendored
Normal file
173
third-party/RakNet/src/RakNet/FileListTransfer.hpp
vendored
Normal file
@@ -0,0 +1,173 @@
|
||||
/// \file FileListTransfer.h
|
||||
/// \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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_FileListTransfer==1 && _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#ifndef __FILE_LIST_TRANFER_H
|
||||
#define __FILE_LIST_TRANFER_H
|
||||
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "Export.hpp"
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "DS_Map.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "PacketPriority.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "FileList.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
#include "SimpleMutex.hpp"
|
||||
#include "ThreadPool.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class IncrementalReadInterface;
|
||||
class FileListTransferCBInterface;
|
||||
class FileListProgress;
|
||||
struct FileListReceiver;
|
||||
|
||||
/// \defgroup FILE_LIST_TRANSFER_GROUP FileListTransfer
|
||||
/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure.
|
||||
/// \details
|
||||
/// \ingroup PLUGINS_GROUP
|
||||
|
||||
/// \brief A plugin to provide a simple way to compress and incrementally send the files in the FileList structure.
|
||||
/// \details 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 PluginInterface2
|
||||
{
|
||||
public:
|
||||
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(FileListTransfer)
|
||||
|
||||
FileListTransfer();
|
||||
virtual ~FileListTransfer();
|
||||
|
||||
/// \brief Optionally start worker threads when using _incrementalReadInterface for the Send() operation
|
||||
/// \param[in] numThreads how many worker threads to start
|
||||
/// \param[in] threadPriority Passed to the thread creation routine. Use THREAD_PRIORITY_NORMAL for Windows. For Linux based systems, you MUST pass something reasonable based on the thread priorities for your application.
|
||||
void StartIncrementalReadThreads(int numThreads, int threadPriority=-99999);
|
||||
|
||||
/// \brief 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);
|
||||
|
||||
/// \brief 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. Pass 0 to use the instance the plugin is attached to
|
||||
/// \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] _incrementalReadInterface If a file in \a fileList has no data, _incrementalReadInterface 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, RakNet::RakPeerInterface *rakPeer, SystemAddress recipient, unsigned short setID, PacketPriority priority, char orderingChannel, IncrementalReadInterface *_incrementalReadInterface=0, unsigned int _chunkSize=262144*4*16);
|
||||
|
||||
/// Return number of files waiting to go out to a particular address
|
||||
unsigned int GetPendingFilesToAddress(SystemAddress recipient);
|
||||
|
||||
/// \brief Stop a download.
|
||||
void CancelReceive(unsigned short setId);
|
||||
|
||||
/// \brief Remove all handlers associated with a particular system address.
|
||||
void RemoveReceiver(SystemAddress systemAddress);
|
||||
|
||||
/// \brief Is a handler passed to SetupReceive still running?
|
||||
bool IsHandlerActive(unsigned short setId);
|
||||
|
||||
/// \brief Adds 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 AddCallback(FileListProgress *cb);
|
||||
|
||||
/// \brief Removes a callback
|
||||
/// \param[in] cb A pointer to an externally defined instance of FileListProgress that was previously added with AddCallback()
|
||||
void RemoveCallback(FileListProgress *cb);
|
||||
|
||||
/// \brief Removes all callbacks
|
||||
void ClearCallbacks(void);
|
||||
|
||||
/// Returns all callbacks added with AddCallback()
|
||||
/// \param[out] callbacks The list is set to the list of callbacks
|
||||
void GetCallbacks(DataStructures::List<FileListProgress*> &callbacks);
|
||||
|
||||
/// \internal For plugin handling
|
||||
virtual PluginReceiveResult OnReceive(Packet *packet);
|
||||
/// \internal For plugin handling
|
||||
virtual void OnRakPeerShutdown(void);
|
||||
/// \internal For plugin handling
|
||||
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
|
||||
/// \internal For plugin handling
|
||||
virtual void Update(void);
|
||||
|
||||
protected:
|
||||
bool DecodeSetHeader(Packet *packet);
|
||||
bool DecodeFile(Packet *packet, bool fullFile);
|
||||
|
||||
void Clear(void);
|
||||
|
||||
void OnReferencePush(Packet *packet, bool fullFile);
|
||||
void OnReferencePushAck(Packet *packet);
|
||||
void SendIRIToAddress(SystemAddress systemAddress);
|
||||
|
||||
DataStructures::Map<unsigned short, FileListReceiver*> fileListReceivers;
|
||||
unsigned short setId;
|
||||
DataStructures::List<FileListProgress*> fileListProgressCallbacks;
|
||||
|
||||
struct FileToPush
|
||||
{
|
||||
FileListNode fileListNode;
|
||||
PacketPriority packetPriority;
|
||||
char orderingChannel;
|
||||
unsigned int currentOffset;
|
||||
unsigned short setID;
|
||||
unsigned int setIndex;
|
||||
IncrementalReadInterface *incrementalReadInterface;
|
||||
unsigned int chunkSize;
|
||||
};
|
||||
struct FileToPushRecipient
|
||||
{
|
||||
unsigned int refCount;
|
||||
SimpleMutex refCountMutex;
|
||||
void DeleteThis(void);
|
||||
void AddRef(void);
|
||||
void Deref(void);
|
||||
|
||||
SystemAddress systemAddress;
|
||||
DataStructures::Queue<FileToPush*> filesToPush;
|
||||
};
|
||||
DataStructures::List< FileToPushRecipient* > fileToPushRecipientList;
|
||||
SimpleMutex fileToPushRecipientListMutex;
|
||||
void RemoveFromList(FileToPushRecipient *ftpr);
|
||||
|
||||
struct ThreadData
|
||||
{
|
||||
FileListTransfer *fileListTransfer;
|
||||
SystemAddress systemAddress;
|
||||
};
|
||||
|
||||
ThreadPool<ThreadData, int> threadPool;
|
||||
|
||||
friend int SendIRIToAddressCB(FileListTransfer::ThreadData threadData, bool *returnOutput, void* perThreadData);
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
154
third-party/RakNet/src/RakNet/FileListTransferCBInterface.hpp
vendored
Normal file
154
third-party/RakNet/src/RakNet/FileListTransferCBInterface.hpp
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/// \file FileListTransferCBInterface.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
|
||||
#define __FILE_LIST_TRANSFER_CALLBACK_INTERFACE_H
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "FileListNodeContext.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
/// \brief Used by FileListTransfer plugin as a callback for when we get a file.
|
||||
/// \details You get the last file when fileIndex==numberOfFilesInThisSet
|
||||
/// \sa FileListTransfer
|
||||
class FileListTransferCBInterface
|
||||
{
|
||||
public:
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
struct OnFileStruct
|
||||
{
|
||||
/// \brief The index into the set of files, from 0 to numberOfFilesInThisSet
|
||||
unsigned fileIndex;
|
||||
|
||||
/// \brief The name of the file
|
||||
char fileName[512];
|
||||
|
||||
/// \brief The data pointed to by the file
|
||||
char *fileData;
|
||||
|
||||
/// \brief The actual length of this file.
|
||||
BitSize_t byteLengthOfThisFile;
|
||||
|
||||
/// \brief How many bytes of this file has been downloaded
|
||||
BitSize_t bytesDownloadedForThisFile;
|
||||
|
||||
/// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time.
|
||||
/// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive
|
||||
unsigned short setID;
|
||||
|
||||
/// \brief The number of files that are in this set.
|
||||
unsigned numberOfFilesInThisSet;
|
||||
|
||||
/// \brief The total length of the transmitted files for this set, after being uncompressed
|
||||
unsigned byteLengthOfThisSet;
|
||||
|
||||
/// \brief The total length, in bytes, downloaded for this set.
|
||||
unsigned bytesDownloadedForThisSet;
|
||||
|
||||
/// \brief User data passed to one of the functions in the FileList class.
|
||||
/// \details However, on error, this is instead changed to one of the enumerations in the PatchContext structure.
|
||||
FileListNodeContext context;
|
||||
|
||||
/// \brief Who sent this file
|
||||
SystemAddress senderSystemAddress;
|
||||
|
||||
/// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP)
|
||||
RakNetGUID senderGuid;
|
||||
};
|
||||
|
||||
// Note: If this structure is changed the struct in the swig files need to be changed as well
|
||||
struct FileProgressStruct
|
||||
{
|
||||
/// \param[out] 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.
|
||||
OnFileStruct *onFileStruct;
|
||||
/// \param[out] partCount The zero based index into partTotal. The percentage complete done of this file is 100 * (partCount+1)/partTotal
|
||||
unsigned int partCount;
|
||||
/// \param[out] 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
|
||||
unsigned int partTotal;
|
||||
/// \param[out] dataChunkLength How many bytes long firstDataChunk and iriDataChunk are
|
||||
unsigned int dataChunkLength;
|
||||
/// \param[out] 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
|
||||
char *firstDataChunk;
|
||||
/// \param[out] iriDataChunk If the remote system is sending this file using IncrementalReadInterface, then this is the chunk we just downloaded. It will not exist in memory after this callback. You should either store this to disk, or in memory. If it is 0, then the file is smaller than one chunk, and will be held in memory automatically
|
||||
char *iriDataChunk;
|
||||
/// \param[out] iriWriteOffset Offset in bytes from the start of the file for the data pointed to by iriDataChunk
|
||||
unsigned int iriWriteOffset;
|
||||
/// \param[out] Who sent this file
|
||||
SystemAddress senderSystemAddress;
|
||||
/// \param[out] Who sent this file. Not valid when using TCP, only RakPeer (UDP)
|
||||
RakNetGUID senderGuid;
|
||||
/// \param[in] allocateIrIDataChunkAutomatically If true, then RakNet will hold iriDataChunk for you and return it in OnFile. Defaults to true
|
||||
bool allocateIrIDataChunkAutomatically;
|
||||
};
|
||||
|
||||
struct DownloadCompleteStruct
|
||||
{
|
||||
/// \brief Files are transmitted in sets, where more than one set of files can be transmitted at the same time.
|
||||
/// \details This is the identifier for the set, which is returned by FileListTransfer::SetupReceive
|
||||
unsigned short setID;
|
||||
|
||||
/// \brief The number of files that are in this set.
|
||||
unsigned numberOfFilesInThisSet;
|
||||
|
||||
/// \brief The total length of the transmitted files for this set, after being uncompressed
|
||||
unsigned byteLengthOfThisSet;
|
||||
|
||||
/// \brief Who sent this file
|
||||
SystemAddress senderSystemAddress;
|
||||
|
||||
/// \brief Who sent this file. Not valid when using TCP, only RakPeer (UDP)
|
||||
RakNetGUID senderGuid;
|
||||
};
|
||||
|
||||
FileListTransferCBInterface() {}
|
||||
virtual ~FileListTransferCBInterface() {}
|
||||
|
||||
/// \brief Got a file.
|
||||
/// \details 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;
|
||||
|
||||
/// \brief Got part of a big file internally in RakNet
|
||||
/// \details This is called in one of two circumstances: Either the transport layer is returning ID_PROGRESS_NOTIFICATION, or you got a block via IncrementalReadInterface
|
||||
/// If the transport layer is returning ID_PROGRESS_NOTIFICATION (see RakPeer::SetSplitMessageProgressInterval()) then FileProgressStruct::iriDataChunk will be 0.
|
||||
/// If this is a block via IncrementalReadInterface, then iriDataChunk will point to the block just downloaded.
|
||||
/// If not using IncrementalReadInterface, then you only care about partCount and partTotal to tell how far the download has progressed. YOu can use firstDataChunk to read the first part of the file if desired. The file is usable when you get the OnFile callback.
|
||||
/// If using IncrementalReadInterface and you let RakNet buffer the files in memory (default), then it is the same as above. The file is usable when you get the OnFile callback.
|
||||
/// If using IncrementalReadInterface and you do not let RakNet buffer the files in memory, then set allocateIrIDataChunkAutomatically to false. Write the file to disk whenever you get OnFileProgress and iriDataChunk is not 0, and ignore OnFile.
|
||||
virtual void OnFileProgress(FileProgressStruct *fps)=0;
|
||||
|
||||
/// \brief Called while the handler is active by FileListTransfer
|
||||
/// \details 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;}
|
||||
|
||||
/// \brief Called when the download is completed.
|
||||
/// \details 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(DownloadCompleteStruct *dcs) {(void) dcs; return false;}
|
||||
|
||||
/// \brief This function is called when this instance is about to be dereferenced by the FileListTransfer plugin.
|
||||
/// \details 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) {}
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
175
third-party/RakNet/src/RakNet/FileOperations.cpp
vendored
Normal file
175
third-party/RakNet/src/RakNet/FileOperations.cpp
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "FileOperations.hpp"
|
||||
#if _RAKNET_SUPPORT_FileOperations==1
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "_FindFirst.hpp" // For linux
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
// For mkdir
|
||||
#include <direct.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include "_FindFirst.hpp"
|
||||
#endif
|
||||
#include "errno.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( disable : 4966 ) // mkdir declared deprecated by Microsoft in order to make it harder to be cross platform. I don't agree it's deprecated.
|
||||
#endif
|
||||
bool WriteFileWithDirectories( const char *path, char *data, unsigned dataLength )
|
||||
{
|
||||
int index;
|
||||
FILE *fp;
|
||||
char *pathCopy;
|
||||
int res;
|
||||
|
||||
if ( path == 0 || path[ 0 ] == 0 )
|
||||
return false;
|
||||
|
||||
pathCopy = (char*) rakMalloc_Ex( strlen( path ) + 1, _FILE_AND_LINE_ );
|
||||
|
||||
strcpy( pathCopy, path );
|
||||
|
||||
// Ignore first / if there is one
|
||||
if (pathCopy[0])
|
||||
{
|
||||
index = 1;
|
||||
while ( pathCopy[ index ] )
|
||||
{
|
||||
if ( pathCopy[ index ] == '/' || pathCopy[ index ] == '\\')
|
||||
{
|
||||
pathCopy[ index ] = 0;
|
||||
|
||||
// BEING Aya CHANGES
|
||||
#ifdef _WIN32
|
||||
res = _mkdir( pathCopy );
|
||||
#else
|
||||
|
||||
res = mkdir( pathCopy, 0744 );
|
||||
#endif
|
||||
// END Aya CHANGES
|
||||
if (res<0 && errno!=EEXIST && errno!=EACCES)
|
||||
{
|
||||
rakFree_Ex(pathCopy, _FILE_AND_LINE_ );
|
||||
return false;
|
||||
}
|
||||
|
||||
pathCopy[ index ] = '/';
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (data)
|
||||
{
|
||||
fp = fopen( path, "wb" );
|
||||
|
||||
if ( fp == 0 )
|
||||
{
|
||||
rakFree_Ex(pathCopy, _FILE_AND_LINE_ );
|
||||
return false;
|
||||
}
|
||||
|
||||
fwrite( data, 1, dataLength, fp );
|
||||
|
||||
fclose( fp );
|
||||
}
|
||||
else
|
||||
{
|
||||
// BEING Aya CHANGES
|
||||
#ifdef _WIN32
|
||||
res = _mkdir( pathCopy );
|
||||
#else
|
||||
res = mkdir( pathCopy, 0744 );
|
||||
#endif
|
||||
// END Aya CHANGES
|
||||
|
||||
if (res<0 && errno!=EEXIST)
|
||||
{
|
||||
rakFree_Ex(pathCopy, _FILE_AND_LINE_ );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rakFree_Ex(pathCopy, _FILE_AND_LINE_ );
|
||||
|
||||
return true;
|
||||
}
|
||||
bool IsSlash(unsigned char c)
|
||||
{
|
||||
return c=='/' || c=='\\';
|
||||
}
|
||||
|
||||
void AddSlash( char *input )
|
||||
{
|
||||
if (input==0 || input[0]==0)
|
||||
return;
|
||||
|
||||
int lastCharIndex=(int) strlen(input)-1;
|
||||
if (input[lastCharIndex]=='\\')
|
||||
input[lastCharIndex]='/';
|
||||
else if (input[lastCharIndex]!='/')
|
||||
{
|
||||
input[lastCharIndex+1]='/';
|
||||
input[lastCharIndex+2]=0;
|
||||
}
|
||||
}
|
||||
bool DirectoryExists(const char *directory)
|
||||
{
|
||||
_finddata_t fileInfo;
|
||||
intptr_t dir;
|
||||
char baseDirWithStars[560];
|
||||
strcpy(baseDirWithStars, directory);
|
||||
AddSlash(baseDirWithStars);
|
||||
strcat(baseDirWithStars, "*.*");
|
||||
dir=_findfirst(baseDirWithStars, &fileInfo );
|
||||
if (dir==-1)
|
||||
return false;
|
||||
_findclose(dir);
|
||||
return true;
|
||||
}
|
||||
void QuoteIfSpaces(char *str)
|
||||
{
|
||||
unsigned i;
|
||||
bool hasSpace=false;
|
||||
for (i=0; str[i]; i++)
|
||||
{
|
||||
if (str[i]==' ')
|
||||
{
|
||||
hasSpace=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasSpace)
|
||||
{
|
||||
int len=(int)strlen(str);
|
||||
memmove(str+1, str, len);
|
||||
str[0]='\"';
|
||||
str[len]='\"';
|
||||
str[len+1]=0;
|
||||
}
|
||||
}
|
||||
unsigned int GetFileLength(const char *path)
|
||||
{
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (fp==0) return 0;
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned int fileLength = ftell(fp);
|
||||
fclose(fp);
|
||||
return fileLength;
|
||||
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_FileOperations
|
||||
|
||||
24
third-party/RakNet/src/RakNet/FileOperations.hpp
vendored
Normal file
24
third-party/RakNet/src/RakNet/FileOperations.hpp
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
/// \file FileOperations.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_FileOperations==1
|
||||
|
||||
#ifndef __FILE_OPERATIONS_H
|
||||
#define __FILE_OPERATIONS_H
|
||||
|
||||
#include "Export.hpp"
|
||||
|
||||
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
|
||||
|
||||
#endif // _RAKNET_SUPPORT_FileOperations
|
||||
30
third-party/RakNet/src/RakNet/FormatString.cpp
vendored
Normal file
30
third-party/RakNet/src/RakNet/FormatString.cpp
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
#include "FormatString.hpp"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
char * FormatString(const char *format, ...)
|
||||
{
|
||||
static int textIndex=0;
|
||||
static char text[4][8096];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
|
||||
if (++textIndex==4)
|
||||
textIndex=0;
|
||||
_vsnprintf(text[textIndex], 8096, format, ap);
|
||||
va_end(ap);
|
||||
text[textIndex][8096-1]=0;
|
||||
|
||||
return text[textIndex];
|
||||
}
|
||||
|
||||
char * FormatStringTS(char *output, const char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
_vsnprintf(output, 512, format, ap);
|
||||
va_end(ap);
|
||||
return output;
|
||||
}
|
||||
22
third-party/RakNet/src/RakNet/FormatString.hpp
vendored
Normal file
22
third-party/RakNet/src/RakNet/FormatString.hpp
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
/// \file FormatString.h
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#ifndef __FORMAT_STRING_H
|
||||
#define __FORMAT_STRING_H
|
||||
|
||||
#include "Export.hpp"
|
||||
|
||||
extern "C" {
|
||||
char * FormatString(const char *format, ...);
|
||||
}
|
||||
// Threadsafe
|
||||
extern "C" {
|
||||
char * FormatStringTS(char *output, const char *format, ...);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
552
third-party/RakNet/src/RakNet/FullyConnectedMesh2.cpp
vendored
Normal file
552
third-party/RakNet/src/RakNet/FullyConnectedMesh2.cpp
vendored
Normal file
@@ -0,0 +1,552 @@
|
||||
/// \file
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_FullyConnectedMesh2==1
|
||||
|
||||
#include "FullyConnectedMesh2.hpp"
|
||||
#include "RakPeerInterface.hpp"
|
||||
#include "MessageIdentifiers.hpp"
|
||||
#include "BitStream.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "GetTime.hpp"
|
||||
#include "Rand.hpp"
|
||||
#include "DS_OrderedList.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
int FCM2ParticipantComp( const FullyConnectedMesh2::FCM2Participant &key, const FullyConnectedMesh2::FCM2Participant &data )
|
||||
{
|
||||
if (key.fcm2Guid < data.fcm2Guid)
|
||||
return -1;
|
||||
if (key.fcm2Guid > data.fcm2Guid)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(FullyConnectedMesh2,FullyConnectedMesh2);
|
||||
|
||||
FullyConnectedMesh2::FullyConnectedMesh2()
|
||||
{
|
||||
startupTime=0;
|
||||
totalConnectionCount=0;
|
||||
ourFCMGuid=0;
|
||||
autoParticipateConnections=true;
|
||||
|
||||
|
||||
|
||||
|
||||
connectOnNewRemoteConnections=true;
|
||||
|
||||
hostRakNetGuid=UNASSIGNED_RAKNET_GUID;
|
||||
}
|
||||
FullyConnectedMesh2::~FullyConnectedMesh2()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
RakNetGUID FullyConnectedMesh2::GetConnectedHost(void) const
|
||||
{
|
||||
if (ourFCMGuid==0)
|
||||
return UNASSIGNED_RAKNET_GUID;
|
||||
return hostRakNetGuid;
|
||||
}
|
||||
SystemAddress FullyConnectedMesh2::GetConnectedHostAddr(void) const
|
||||
{
|
||||
if (ourFCMGuid==0)
|
||||
return UNASSIGNED_SYSTEM_ADDRESS;
|
||||
return rakPeerInterface->GetSystemAddressFromGuid(hostRakNetGuid);
|
||||
}
|
||||
RakNetGUID FullyConnectedMesh2::GetHostSystem(void) const
|
||||
{
|
||||
if (ourFCMGuid==0)
|
||||
return rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
return hostRakNetGuid;
|
||||
}
|
||||
bool FullyConnectedMesh2::IsHostSystem(void) const
|
||||
{
|
||||
return GetHostSystem()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
void FullyConnectedMesh2::GetHostOrder(DataStructures::List<RakNetGUID> &hostList)
|
||||
{
|
||||
hostList.Clear(true, _FILE_AND_LINE_);
|
||||
|
||||
if (ourFCMGuid==0 || fcm2ParticipantList.Size()==0)
|
||||
{
|
||||
hostList.Push(rakPeerInterface->GetMyGUID(), _FILE_AND_LINE_);
|
||||
return;
|
||||
}
|
||||
|
||||
FCM2Participant fcm2;
|
||||
fcm2.fcm2Guid=ourFCMGuid;
|
||||
fcm2.rakNetGuid=rakPeerInterface->GetMyGUID();
|
||||
|
||||
DataStructures::OrderedList<FCM2Participant, FCM2Participant, FCM2ParticipantComp> olist;
|
||||
olist.Insert(fcm2, fcm2, true, _FILE_AND_LINE_);
|
||||
for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++)
|
||||
olist.Insert(fcm2ParticipantList[i], fcm2ParticipantList[i], true, _FILE_AND_LINE_);
|
||||
|
||||
for (unsigned int i=0; i < olist.Size(); i++)
|
||||
{
|
||||
hostList.Push(olist[i].rakNetGuid, _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
bool FullyConnectedMesh2::IsConnectedHost(void) const
|
||||
{
|
||||
return GetConnectedHost()==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
}
|
||||
void FullyConnectedMesh2::SetAutoparticipateConnections(bool b)
|
||||
{
|
||||
autoParticipateConnections=b;
|
||||
}
|
||||
void FullyConnectedMesh2::ResetHostCalculation(void)
|
||||
{
|
||||
hostRakNetGuid=UNASSIGNED_RAKNET_GUID;
|
||||
startupTime=RakNet::GetTimeUS();
|
||||
totalConnectionCount=0;
|
||||
ourFCMGuid=0;
|
||||
for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++)
|
||||
SendFCMGuidRequest(fcm2ParticipantList[i].rakNetGuid);
|
||||
}
|
||||
bool FullyConnectedMesh2::AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid )
|
||||
{
|
||||
for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++)
|
||||
{
|
||||
if (fcm2ParticipantList[i].rakNetGuid==rakNetGuid)
|
||||
{
|
||||
if (theirFCMGuid!=0)
|
||||
fcm2ParticipantList[i].fcm2Guid=theirFCMGuid;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FCM2Participant participant;
|
||||
participant.rakNetGuid=rakNetGuid;
|
||||
participant.fcm2Guid=theirFCMGuid;
|
||||
fcm2ParticipantList.Push(participant,_FILE_AND_LINE_);
|
||||
|
||||
SendFCMGuidRequest(rakNetGuid);
|
||||
|
||||
return true;
|
||||
}
|
||||
void FullyConnectedMesh2::AddParticipant( RakNetGUID rakNetGuid )
|
||||
{
|
||||
if (rakPeerInterface->GetConnectionState(rakPeerInterface->GetSystemAddressFromGuid(rakNetGuid))!=IS_CONNECTED)
|
||||
{
|
||||
#ifdef DEBUG_FCM2
|
||||
printf("AddParticipant to %s failed (not connected)\n", rakNetGuid.ToString());
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
AddParticipantInternal(rakNetGuid,0);
|
||||
}
|
||||
void FullyConnectedMesh2::GetParticipantList(DataStructures::List<RakNetGUID> &participantList)
|
||||
{
|
||||
participantList.Clear(true, _FILE_AND_LINE_);
|
||||
unsigned int i;
|
||||
for (i=0; i < fcm2ParticipantList.Size(); i++)
|
||||
participantList.Push(fcm2ParticipantList[i].rakNetGuid, _FILE_AND_LINE_);
|
||||
}
|
||||
PluginReceiveResult FullyConnectedMesh2::OnReceive(Packet *packet)
|
||||
{
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_REMOTE_NEW_INCOMING_CONNECTION:
|
||||
{
|
||||
if (connectOnNewRemoteConnections)
|
||||
ConnectToRemoteNewIncomingConnections(packet);
|
||||
}
|
||||
break;
|
||||
case ID_FCM2_REQUEST_FCMGUID:
|
||||
OnRequestFCMGuid(packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
case ID_FCM2_RESPOND_CONNECTION_COUNT:
|
||||
OnRespondConnectionCount(packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
case ID_FCM2_INFORM_FCMGUID:
|
||||
OnInformFCMGuid(packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
case ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT:
|
||||
OnUpdateMinTotalConnectionCount(packet);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
case ID_FCM2_NEW_HOST:
|
||||
if (packet->wasGeneratedLocally==false)
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
break;
|
||||
}
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
void FullyConnectedMesh2::OnRakPeerStartup(void)
|
||||
{
|
||||
Clear();
|
||||
startupTime=RakNet::GetTimeUS();
|
||||
}
|
||||
void FullyConnectedMesh2::OnAttach(void)
|
||||
{
|
||||
Clear();
|
||||
// In case Startup() was called first
|
||||
if (rakPeerInterface->IsActive())
|
||||
startupTime=RakNet::GetTimeUS();
|
||||
}
|
||||
void FullyConnectedMesh2::OnRakPeerShutdown(void)
|
||||
{
|
||||
Clear();
|
||||
startupTime=0;
|
||||
}
|
||||
void FullyConnectedMesh2::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
||||
{
|
||||
(void) lostConnectionReason;
|
||||
(void) systemAddress;
|
||||
(void) rakNetGUID;
|
||||
|
||||
unsigned int idx;
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
{
|
||||
if (fcm2ParticipantList[idx].rakNetGuid==rakNetGUID)
|
||||
{
|
||||
fcm2ParticipantList[idx]=fcm2ParticipantList[fcm2ParticipantList.Size()-1];
|
||||
#ifdef DEBUG_FCM2
|
||||
printf("Popping participant %s\n", fcm2ParticipantList[fcm2ParticipantList.Size()-1].rakNetGuid.ToString());
|
||||
#endif
|
||||
|
||||
fcm2ParticipantList.Pop();
|
||||
if (rakNetGUID==hostRakNetGuid && ourFCMGuid!=0)
|
||||
{
|
||||
if (fcm2ParticipantList.Size()==0)
|
||||
{
|
||||
hostRakNetGuid=rakPeerInterface->GetMyGUID();
|
||||
hostFCM2Guid=ourFCMGuid;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateHost(&hostRakNetGuid, &hostFCM2Guid);
|
||||
}
|
||||
PushNewHost(hostRakNetGuid, rakNetGUID);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
RakNet::TimeUS FullyConnectedMesh2::GetElapsedRuntime(void)
|
||||
{
|
||||
RakNet::TimeUS curTime=RakNet::GetTimeUS();
|
||||
if (curTime>startupTime)
|
||||
return curTime-startupTime;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
void FullyConnectedMesh2::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
||||
{
|
||||
(void) isIncoming;
|
||||
(void) rakNetGUID;
|
||||
(void) systemAddress;
|
||||
|
||||
if (autoParticipateConnections)
|
||||
AddParticipant(rakNetGUID);
|
||||
}
|
||||
void FullyConnectedMesh2::Clear(void)
|
||||
{
|
||||
fcm2ParticipantList.Clear(false, _FILE_AND_LINE_);
|
||||
|
||||
totalConnectionCount=0;
|
||||
ourFCMGuid=0;
|
||||
lastPushedHost=UNASSIGNED_RAKNET_GUID;
|
||||
}
|
||||
void FullyConnectedMesh2::PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost)
|
||||
{
|
||||
Packet *p = AllocatePacketUnified(sizeof(MessageID)+sizeof(oldHost));
|
||||
RakNet::BitStream bs(p->data,p->length,false);
|
||||
bs.SetWriteOffset(0);
|
||||
bs.Write((MessageID)ID_FCM2_NEW_HOST);
|
||||
bs.Write(oldHost);
|
||||
p->systemAddress=rakPeerInterface->GetSystemAddressFromGuid(guid);
|
||||
p->systemAddress.systemIndex=(SystemIndex)-1;
|
||||
p->guid=guid;
|
||||
p->wasGeneratedLocally=true;
|
||||
rakPeerInterface->PushBackPacket(p, true);
|
||||
|
||||
lastPushedHost=guid;
|
||||
}
|
||||
void FullyConnectedMesh2::SendFCMGuidRequest(RakNetGUID rakNetGuid)
|
||||
{
|
||||
if (rakNetGuid==rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS))
|
||||
return;
|
||||
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_FCM2_REQUEST_FCMGUID);
|
||||
if (ourFCMGuid==0)
|
||||
{
|
||||
bsOut.Write(false);
|
||||
bsOut.Write(GetElapsedRuntime());
|
||||
}
|
||||
else
|
||||
{
|
||||
bsOut.Write(true);
|
||||
bsOut.Write(totalConnectionCount);
|
||||
bsOut.Write(ourFCMGuid);
|
||||
}
|
||||
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,rakNetGuid,false);
|
||||
}
|
||||
void FullyConnectedMesh2::SendOurFCMGuid(SystemAddress addr)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_FCM2_INFORM_FCMGUID);
|
||||
RakAssert(ourFCMGuid!=0); // Can't inform others of our FCM2Guid if it's unset!
|
||||
bsOut.Write(ourFCMGuid);
|
||||
bsOut.Write(totalConnectionCount);
|
||||
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false);
|
||||
}
|
||||
void FullyConnectedMesh2::SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount)
|
||||
{
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_FCM2_RESPOND_CONNECTION_COUNT);
|
||||
bsOut.Write(responseTotalConnectionCount);
|
||||
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,addr,false);
|
||||
}
|
||||
void FullyConnectedMesh2::AssignOurFCMGuid(void)
|
||||
{
|
||||
// Only assigned once ever
|
||||
RakAssert(ourFCMGuid==0);
|
||||
unsigned int randomNumber = randomMT();
|
||||
randomNumber ^= (unsigned int) (RakNet::GetTimeUS() & 0xFFFFFFFF);
|
||||
randomNumber ^= (unsigned int) (rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS).g & 0xFFFFFFFF);
|
||||
ourFCMGuid |= randomNumber;
|
||||
uint64_t reponse64 = totalConnectionCount;
|
||||
ourFCMGuid |= reponse64<<32;
|
||||
}
|
||||
void FullyConnectedMesh2::CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid)
|
||||
{
|
||||
// Can't calculate host without knowing our own
|
||||
RakAssert(ourFCMGuid!=0);
|
||||
|
||||
// Can't calculate host without being connected to anyone else
|
||||
RakAssert(fcm2ParticipantList.Size()>0);
|
||||
|
||||
// Return the lowest value of all FCM2Guid
|
||||
FCM2Guid lowestFCMGuid=ourFCMGuid;
|
||||
// SystemAddress associatedSystemAddress=UNASSIGNED_SYSTEM_ADDRESS;
|
||||
RakNetGUID associatedRakNetGuid=rakPeerInterface->GetGuidFromSystemAddress(UNASSIGNED_SYSTEM_ADDRESS);
|
||||
|
||||
unsigned int idx;
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
{
|
||||
if (fcm2ParticipantList[idx].fcm2Guid!=0 && fcm2ParticipantList[idx].fcm2Guid<lowestFCMGuid)
|
||||
{
|
||||
lowestFCMGuid=fcm2ParticipantList[idx].fcm2Guid;
|
||||
associatedRakNetGuid=fcm2ParticipantList[idx].rakNetGuid;
|
||||
}
|
||||
}
|
||||
|
||||
*rakNetGuid=associatedRakNetGuid;
|
||||
*fcm2Guid=lowestFCMGuid;
|
||||
}
|
||||
void FullyConnectedMesh2::OnRequestFCMGuid(Packet *packet)
|
||||
{
|
||||
RakNet::BitStream bsIn(packet->data,packet->length,false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
bool hasRemoteFCMGuid=false;
|
||||
bsIn.Read(hasRemoteFCMGuid);
|
||||
RakNet::TimeUS senderElapsedRuntime=0;
|
||||
unsigned int remoteTotalConnectionCount=0;
|
||||
FCM2Guid theirFCMGuid=0;
|
||||
if (hasRemoteFCMGuid)
|
||||
{
|
||||
bsIn.Read(remoteTotalConnectionCount);
|
||||
bsIn.Read(theirFCMGuid);
|
||||
}
|
||||
else
|
||||
{
|
||||
bsIn.Read(senderElapsedRuntime);
|
||||
}
|
||||
AddParticipantInternal(packet->guid,theirFCMGuid);
|
||||
if (ourFCMGuid==0)
|
||||
{
|
||||
if (hasRemoteFCMGuid==false)
|
||||
{
|
||||
// Nobody has a fcmGuid
|
||||
|
||||
RakNet::TimeUS ourElapsedRuntime = GetElapsedRuntime();
|
||||
if (ourElapsedRuntime>senderElapsedRuntime)
|
||||
{
|
||||
// We are probably host
|
||||
SendConnectionCountResponse(packet->systemAddress, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// They are probably host
|
||||
SendConnectionCountResponse(packet->systemAddress, 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// They have a fcmGuid, we do not
|
||||
IncrementTotalConnectionCount(remoteTotalConnectionCount+1);
|
||||
|
||||
AssignOurFCMGuid();
|
||||
unsigned int idx;
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hasRemoteFCMGuid==false)
|
||||
{
|
||||
// We have a fcmGuid they do not
|
||||
SendConnectionCountResponse(packet->systemAddress, totalConnectionCount+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We both have fcmGuids
|
||||
IncrementTotalConnectionCount(remoteTotalConnectionCount);
|
||||
|
||||
SendOurFCMGuid(packet->systemAddress);
|
||||
}
|
||||
}
|
||||
CalculateAndPushHost();
|
||||
}
|
||||
void FullyConnectedMesh2::OnRespondConnectionCount(Packet *packet)
|
||||
{
|
||||
RakNet::BitStream bsIn(packet->data,packet->length,false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
unsigned int responseTotalConnectionCount;
|
||||
bsIn.Read(responseTotalConnectionCount);
|
||||
IncrementTotalConnectionCount(responseTotalConnectionCount);
|
||||
bool wasAssigned;
|
||||
if (ourFCMGuid==0)
|
||||
{
|
||||
wasAssigned=true;
|
||||
AssignOurFCMGuid();
|
||||
}
|
||||
else
|
||||
wasAssigned=false;
|
||||
|
||||
// 1 is returned to give us lower priority, but the actual minimum is 2
|
||||
IncrementTotalConnectionCount(2);
|
||||
|
||||
if (wasAssigned==true)
|
||||
{
|
||||
unsigned int idx;
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid));
|
||||
CalculateAndPushHost();
|
||||
}
|
||||
}
|
||||
void FullyConnectedMesh2::OnInformFCMGuid(Packet *packet)
|
||||
{
|
||||
RakNet::BitStream bsIn(packet->data,packet->length,false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
|
||||
FCM2Guid theirFCMGuid;
|
||||
unsigned int theirTotalConnectionCount;
|
||||
bsIn.Read(theirFCMGuid);
|
||||
bsIn.Read(theirTotalConnectionCount);
|
||||
IncrementTotalConnectionCount(theirTotalConnectionCount);
|
||||
|
||||
if (AddParticipantInternal(packet->guid,theirFCMGuid))
|
||||
{
|
||||
// 1/19/2010 - Relay increased total connection count in case new participant only connects to part of the mesh
|
||||
unsigned int idx;
|
||||
RakNet::BitStream bsOut;
|
||||
bsOut.Write((MessageID)ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT);
|
||||
bsOut.Write(totalConnectionCount);
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
{
|
||||
if (packet->guid!=fcm2ParticipantList[idx].rakNetGuid)
|
||||
rakPeerInterface->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,fcm2ParticipantList[idx].rakNetGuid,false);
|
||||
}
|
||||
}
|
||||
|
||||
if (ourFCMGuid==0)
|
||||
{
|
||||
AssignOurFCMGuid();
|
||||
unsigned int idx;
|
||||
for (idx=0; idx < fcm2ParticipantList.Size(); idx++)
|
||||
SendOurFCMGuid(rakPeerInterface->GetSystemAddressFromGuid(fcm2ParticipantList[idx].rakNetGuid));
|
||||
}
|
||||
|
||||
CalculateAndPushHost();
|
||||
}
|
||||
void FullyConnectedMesh2::OnUpdateMinTotalConnectionCount(Packet *packet)
|
||||
{
|
||||
RakNet::BitStream bsIn(packet->data,packet->length,false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
unsigned int newMin;
|
||||
bsIn.Read(newMin);
|
||||
IncrementTotalConnectionCount(newMin);
|
||||
}
|
||||
void FullyConnectedMesh2::GetParticipantCount(unsigned int *participantListSize) const
|
||||
{
|
||||
*participantListSize=fcm2ParticipantList.Size();
|
||||
}
|
||||
|
||||
unsigned int FullyConnectedMesh2::GetParticipantCount(void) const
|
||||
{
|
||||
return fcm2ParticipantList.Size();
|
||||
}
|
||||
void FullyConnectedMesh2::CalculateAndPushHost(void)
|
||||
{
|
||||
RakNetGUID newHostGuid;
|
||||
FCM2Guid newFcmGuid;
|
||||
if (ParticipantListComplete())
|
||||
{
|
||||
CalculateHost(&newHostGuid, &newFcmGuid);
|
||||
if (newHostGuid!=lastPushedHost)
|
||||
{
|
||||
hostRakNetGuid=newHostGuid;
|
||||
hostFCM2Guid=newFcmGuid;
|
||||
PushNewHost(hostRakNetGuid, hostRakNetGuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool FullyConnectedMesh2::ParticipantListComplete(void)
|
||||
{
|
||||
for (unsigned int i=0; i < fcm2ParticipantList.Size(); i++)
|
||||
{
|
||||
if (fcm2ParticipantList[i].fcm2Guid==0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void FullyConnectedMesh2::IncrementTotalConnectionCount(unsigned int i)
|
||||
{
|
||||
if (i>totalConnectionCount)
|
||||
{
|
||||
totalConnectionCount=i;
|
||||
// printf("totalConnectionCount=%i\n",i);
|
||||
}
|
||||
}
|
||||
void FullyConnectedMesh2::SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw)
|
||||
{
|
||||
connectOnNewRemoteConnections=attemptConnection;
|
||||
connectionPassword=pw;
|
||||
}
|
||||
|
||||
void FullyConnectedMesh2::ConnectToRemoteNewIncomingConnections(Packet *packet)
|
||||
{
|
||||
unsigned int count;
|
||||
RakNet::BitStream bsIn(packet->data, packet->length, false);
|
||||
bsIn.IgnoreBytes(sizeof(MessageID));
|
||||
bsIn.Read(count);
|
||||
SystemAddress remoteAddress;
|
||||
RakNetGUID remoteGuid;
|
||||
char str[64];
|
||||
for (unsigned int i=0; i < count; i++)
|
||||
{
|
||||
bsIn.Read(remoteAddress);
|
||||
bsIn.Read(remoteGuid);
|
||||
remoteAddress.ToString(false,str);
|
||||
rakPeerInterface->Connect(str,remoteAddress.GetPort(),connectionPassword.C_String(),(int) connectionPassword.GetLength());
|
||||
}
|
||||
}
|
||||
unsigned int FullyConnectedMesh2::GetTotalConnectionCount(void) const
|
||||
{
|
||||
return totalConnectionCount;
|
||||
}
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
253
third-party/RakNet/src/RakNet/FullyConnectedMesh2.hpp
vendored
Normal file
253
third-party/RakNet/src/RakNet/FullyConnectedMesh2.hpp
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
/// \file FullyConnectedMesh2.h
|
||||
/// \brief Fully connected mesh plugin, revision 2.
|
||||
/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_FullyConnectedMesh2==1
|
||||
|
||||
#ifndef __FULLY_CONNECTED_MESH_2_H
|
||||
#define __FULLY_CONNECTED_MESH_2_H
|
||||
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "NativeTypes.hpp"
|
||||
#include "DS_List.hpp"
|
||||
#include "RakString.hpp"
|
||||
|
||||
typedef int64_t FCM2Guid;
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
|
||||
/// \brief Fully connected mesh plugin, revision 2
|
||||
/// \details This will connect RakPeer to all connecting peers, and all peers the connecting peer knows about.<BR>
|
||||
/// It will also calculate which system has been running longest, to find out who should be host, if you need one system to act as a host
|
||||
/// \pre You must also install the ConnectionGraph2 plugin in order to use SetConnectOnNewRemoteConnection()
|
||||
/// \ingroup FULLY_CONNECTED_MESH_GROUP
|
||||
class RAK_DLL_EXPORT FullyConnectedMesh2 : public PluginInterface2
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(FullyConnectedMesh2)
|
||||
|
||||
FullyConnectedMesh2();
|
||||
virtual ~FullyConnectedMesh2();
|
||||
|
||||
/// When the message ID_REMOTE_NEW_INCOMING_CONNECTION arrives, we try to connect to that system
|
||||
/// If \a attemptConnection is false, you can manually connect to all systems listed in ID_REMOTE_NEW_INCOMING_CONNECTION with ConnectToRemoteNewIncomingConnections()
|
||||
/// \note This will not work on any console. It will also not work if NAT punchthrough is needed. Generally, this should be false and you should connect manually. It is here for legacy reasons.
|
||||
/// \param[in] attemptConnection If true, we try to connect to any systems we are notified about with ID_REMOTE_NEW_INCOMING_CONNECTION, which comes from the ConnectionGraph2 plugin. Defaults to true.
|
||||
/// \param[in] pw The password to use to connect with. Only used if \a attemptConnection is true
|
||||
void SetConnectOnNewRemoteConnection(bool attemptConnection, RakNet::RakString pw);
|
||||
|
||||
/// \brief The connected host is whichever system we are connected to that has been running the longest.
|
||||
/// \details Will return UNASSIGNED_RAKNET_GUID if we are not connected to anyone, or if we are connected and are calculating the host
|
||||
/// If includeCalculating is true, will return the estimated calculated host as long as the calculation is nearly complete
|
||||
/// includeCalculating should be true if you are taking action based on another system becoming host, because not all host calculations may complete at the exact same time
|
||||
/// \sa ConnectionGraph2::GetLowestAveragePingSystem() . If you need one system in the peer to peer group to relay data, have the host call this function after host migration, and use that system
|
||||
/// \return System address of whichever system is host.
|
||||
RakNetGUID GetConnectedHost(void) const;
|
||||
SystemAddress GetConnectedHostAddr(void) const;
|
||||
|
||||
/// \return System address of whichever system is host. Always returns something, even though it may be our own system.
|
||||
RakNetGUID GetHostSystem(void) const;
|
||||
|
||||
/// \return If our system is host
|
||||
bool IsHostSystem(void) const;
|
||||
|
||||
/// Get the list of connected systems, from oldest connected to newest
|
||||
/// This is also the order that the hosts will be chosen in
|
||||
void GetHostOrder(DataStructures::List<RakNetGUID> &hostList);
|
||||
|
||||
/// \param[in] includeCalculating If true, and we are currently calculating a new host, return the new host if the calculation is nearly complete
|
||||
/// \return If our system is host
|
||||
bool IsConnectedHost(void) const;
|
||||
|
||||
/// \brief Automatically add new connections to the fully connected mesh.
|
||||
/// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function
|
||||
/// \details Defaults to true.
|
||||
/// \param[in] b As stated
|
||||
void SetAutoparticipateConnections(bool b);
|
||||
|
||||
/// Clear our own host order, and recalculate as if we had just reconnected
|
||||
/// Call this to reset the running time of the host just before joining/creating a game room for networking
|
||||
void ResetHostCalculation(void);
|
||||
|
||||
/// \brief if SetAutoparticipateConnections() is called with false, then you need to use AddParticipant before these systems will be added to the mesh
|
||||
/// FullyConnectedMesh2 will track who is the who host among a fully connected mesh of participants
|
||||
/// Each remote system that you want to check should be added as a participant, either through SetAutoparticipateConnections() or by calling this function
|
||||
/// \param[in] participant The new participant
|
||||
void AddParticipant(RakNetGUID rakNetGuid);
|
||||
|
||||
/// Get the participants added with AddParticipant()
|
||||
/// \param[out] participantList Participants added with AddParticipant();
|
||||
void GetParticipantList(DataStructures::List<RakNetGUID> &participantList);
|
||||
|
||||
/// Connect to all systems from ID_REMOTE_NEW_INCOMING_CONNECTION
|
||||
/// You can call this if SetConnectOnNewRemoteConnection is false
|
||||
/// \param[in] packet The packet containing ID_REMOTE_NEW_INCOMING_CONNECTION
|
||||
/// \param[in] connectionPassword Password passed to RakPeerInterface::Connect()
|
||||
/// \param[in] connectionPasswordLength Password length passed to RakPeerInterface::Connect()
|
||||
void ConnectToRemoteNewIncomingConnections(Packet *packet);
|
||||
|
||||
/// \brief Clear all memory and reset everything
|
||||
void Clear(void);
|
||||
|
||||
unsigned int GetParticipantCount(void) const;
|
||||
void GetParticipantCount(unsigned int *participantListSize) const;
|
||||
/// \internal
|
||||
RakNet::TimeUS GetElapsedRuntime(void);
|
||||
|
||||
/// \internal
|
||||
virtual PluginReceiveResult OnReceive(Packet *packet);
|
||||
/// \internal
|
||||
virtual void OnRakPeerStartup(void);
|
||||
/// \internal
|
||||
virtual void OnAttach(void);
|
||||
/// \internal
|
||||
virtual void OnRakPeerShutdown(void);
|
||||
/// \internal
|
||||
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
|
||||
/// \internal
|
||||
virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
|
||||
|
||||
/// \internal
|
||||
struct FCM2Participant
|
||||
{
|
||||
FCM2Participant() {}
|
||||
FCM2Participant(const FCM2Guid &_fcm2Guid, const RakNetGUID &_rakNetGuid) : fcm2Guid(_fcm2Guid), rakNetGuid(_rakNetGuid) {}
|
||||
|
||||
// Low half is a random number.
|
||||
// High half is the order we connected in (totalConnectionCount)
|
||||
FCM2Guid fcm2Guid;
|
||||
RakNetGUID rakNetGuid;
|
||||
};
|
||||
|
||||
/// \internal for debugging
|
||||
unsigned int GetTotalConnectionCount(void) const;
|
||||
|
||||
protected:
|
||||
void PushNewHost(const RakNetGUID &guid, RakNetGUID oldHost);
|
||||
void SendOurFCMGuid(SystemAddress addr);
|
||||
void SendFCMGuidRequest(RakNetGUID rakNetGuid);
|
||||
void SendConnectionCountResponse(SystemAddress addr, unsigned int responseTotalConnectionCount);
|
||||
void OnRequestFCMGuid(Packet *packet);
|
||||
void OnRespondConnectionCount(Packet *packet);
|
||||
void OnInformFCMGuid(Packet *packet);
|
||||
void OnUpdateMinTotalConnectionCount(Packet *packet);
|
||||
void AssignOurFCMGuid(void);
|
||||
void CalculateHost(RakNetGUID *rakNetGuid, FCM2Guid *fcm2Guid);
|
||||
bool AddParticipantInternal( RakNetGUID rakNetGuid, FCM2Guid theirFCMGuid );
|
||||
void CalculateAndPushHost(void);
|
||||
bool ParticipantListComplete(void);
|
||||
void IncrementTotalConnectionCount(unsigned int i);
|
||||
|
||||
// Used to track how long RakNet has been running. This is so we know who has been running longest
|
||||
RakNet::TimeUS startupTime;
|
||||
|
||||
// Option for SetAutoparticipateConnections
|
||||
bool autoParticipateConnections;
|
||||
|
||||
// totalConnectionCount is roughly maintained across all systems, and increments by 1 each time a new system connects to the mesh
|
||||
// It is always kept at the highest known value
|
||||
// It is used as the high 4 bytes for new FCMGuids. This causes newer values of FCM2Guid to be higher than lower values. The lowest value is the host.
|
||||
unsigned int totalConnectionCount;
|
||||
|
||||
// Our own ourFCMGuid. Starts at unassigned (0). Assigned once we send ID_FCM2_REQUEST_FCMGUID and get back ID_FCM2_RESPOND_CONNECTION_COUNT
|
||||
FCM2Guid ourFCMGuid;
|
||||
|
||||
/// List of systems we know the FCM2Guid for
|
||||
DataStructures::List<FCM2Participant> fcm2ParticipantList;
|
||||
|
||||
RakNetGUID lastPushedHost;
|
||||
|
||||
// Optimization: Store last calculated host in these variables.
|
||||
RakNetGUID hostRakNetGuid;
|
||||
FCM2Guid hostFCM2Guid;
|
||||
|
||||
RakNet::RakString connectionPassword;
|
||||
bool connectOnNewRemoteConnections;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
/*
|
||||
Startup()
|
||||
ourFCMGuid=unknown
|
||||
totalConnectionCount=0
|
||||
Set startupTime
|
||||
|
||||
AddParticipant()
|
||||
if (sender by guid is a participant)
|
||||
return;
|
||||
AddParticipantInternal(guid);
|
||||
if (ourFCMGuid==unknown)
|
||||
Send to that system a request for their fcmGuid, totalConnectionCount. Inform startupTime.
|
||||
else
|
||||
Send to that system a request for their fcmGuid. Inform total connection count, our fcmGuid
|
||||
|
||||
OnRequestGuid()
|
||||
if (sender by guid is not a participant)
|
||||
{
|
||||
// They added us as a participant, but we didn't add them. This can be caused by lag where both participants are not added at the same time.
|
||||
// It doesn't affect the outcome as long as we still process the data
|
||||
AddParticipantInternal(guid);
|
||||
}
|
||||
if (ourFCMGuid==unknown)
|
||||
{
|
||||
if (includedStartupTime)
|
||||
{
|
||||
// Nobody has a fcmGuid
|
||||
|
||||
if (their startup time is greater than our startup time)
|
||||
ReplyConnectionCount(1);
|
||||
else
|
||||
ReplyConnectionCount(2);
|
||||
}
|
||||
else
|
||||
{
|
||||
// They have a fcmGuid, we do not
|
||||
|
||||
SetMaxTotalConnectionCount(remoteCount);
|
||||
AssignTheirGuid()
|
||||
GenerateOurGuid();
|
||||
SendOurGuid(all);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (includedStartupTime)
|
||||
{
|
||||
// We have a fcmGuid they do not
|
||||
|
||||
ReplyConnectionCount(totalConnectionCount+1);
|
||||
SendOurGuid(sender);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We both have fcmGuids
|
||||
|
||||
SetMaxTotalConnectionCount(remoteCount);
|
||||
AssignTheirGuid();
|
||||
SendOurGuid(sender);
|
||||
}
|
||||
}
|
||||
|
||||
OnReplyConnectionCount()
|
||||
SetMaxTotalConnectionCount(remoteCount);
|
||||
GenerateOurGuid();
|
||||
SendOurGuid(allParticipants);
|
||||
|
||||
OnReceiveTheirGuid()
|
||||
AssignTheirGuid()
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
220
third-party/RakNet/src/RakNet/GetTime.cpp
vendored
Normal file
220
third-party/RakNet/src/RakNet/GetTime.cpp
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
/// \file
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include "WindowsIncludes.hpp"
|
||||
// To call timeGetTime
|
||||
// on Code::Blocks, this needs to be libwinmm.a instead
|
||||
// #pragma comment(lib, "Winmm.lib") LINK AGAINST WINMM IN PROJECT SETTINGS!
|
||||
#endif
|
||||
|
||||
#include "GetTime.hpp"
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
DWORD mProcMask;
|
||||
DWORD mSysMask;
|
||||
HANDLE mThread;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
RakNet::TimeUS initialTime;
|
||||
#endif
|
||||
|
||||
static bool initialized=false;
|
||||
|
||||
#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
#include "SimpleMutex.hpp"
|
||||
RakNet::TimeUS lastNormalizedReturnedValue=0;
|
||||
RakNet::TimeUS lastNormalizedInputValue=0;
|
||||
/// This constraints timer forward jumps to 1 second, and does not let it jump backwards
|
||||
/// See http://support.microsoft.com/kb/274323 where the timer can sometimes jump forward by hours or days
|
||||
/// This also has the effect where debugging a sending system won't treat the time spent halted past 1 second as elapsed network time
|
||||
RakNet::TimeUS NormalizeTime(RakNet::TimeUS timeIn)
|
||||
{
|
||||
RakNet::TimeUS diff, lastNormalizedReturnedValueCopy;
|
||||
static RakNet::SimpleMutex mutex;
|
||||
|
||||
mutex.Lock();
|
||||
if (timeIn>=lastNormalizedInputValue)
|
||||
{
|
||||
diff = timeIn-lastNormalizedInputValue;
|
||||
if (diff > GET_TIME_SPIKE_LIMIT)
|
||||
lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT;
|
||||
else
|
||||
lastNormalizedReturnedValue+=diff;
|
||||
}
|
||||
else
|
||||
lastNormalizedReturnedValue+=GET_TIME_SPIKE_LIMIT;
|
||||
|
||||
lastNormalizedInputValue=timeIn;
|
||||
lastNormalizedReturnedValueCopy=lastNormalizedReturnedValue;
|
||||
mutex.Unlock();
|
||||
|
||||
return lastNormalizedReturnedValueCopy;
|
||||
}
|
||||
#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
RakNet::Time RakNet::GetTime( void )
|
||||
{
|
||||
return (RakNet::Time)(GetTimeUS()/1000);
|
||||
}
|
||||
RakNet::TimeMS RakNet::GetTimeMS( void )
|
||||
{
|
||||
return (RakNet::TimeMS)(GetTimeUS()/1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
RakNet::TimeUS GetTimeUS_Windows( void )
|
||||
{
|
||||
if ( initialized == false)
|
||||
{
|
||||
initialized = true;
|
||||
|
||||
// Save the current process
|
||||
#if !defined(_WIN32_WCE)
|
||||
HANDLE mProc = GetCurrentProcess();
|
||||
|
||||
// Get the current Affinity
|
||||
#if _MSC_VER >= 1400 && (defined (_M_X64) || defined(_M_ARM64))
|
||||
GetProcessAffinityMask(mProc, (PDWORD_PTR)&mProcMask, (PDWORD_PTR)&mSysMask);
|
||||
#else
|
||||
GetProcessAffinityMask(mProc, &mProcMask, &mSysMask);
|
||||
#endif
|
||||
mThread = GetCurrentThread();
|
||||
|
||||
#endif // _WIN32_WCE
|
||||
}
|
||||
|
||||
// 9/26/2010 In China running LuDaShi, QueryPerformanceFrequency has to be called every time because CPU clock speeds can be different
|
||||
RakNet::TimeUS curTime;
|
||||
LARGE_INTEGER PerfVal;
|
||||
LARGE_INTEGER yo1;
|
||||
|
||||
QueryPerformanceFrequency( &yo1 );
|
||||
QueryPerformanceCounter( &PerfVal );
|
||||
|
||||
__int64 quotient, remainder;
|
||||
quotient=((PerfVal.QuadPart) / yo1.QuadPart);
|
||||
remainder=((PerfVal.QuadPart) % yo1.QuadPart);
|
||||
curTime = (RakNet::TimeUS) quotient*(RakNet::TimeUS)1000000 + (remainder*(RakNet::TimeUS)1000000 / yo1.QuadPart);
|
||||
|
||||
#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
return NormalizeTime(curTime);
|
||||
#else
|
||||
return curTime;
|
||||
#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
}
|
||||
#elif defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)
|
||||
RakNet::TimeUS GetTimeUS_Linux( void )
|
||||
{
|
||||
timeval tp;
|
||||
if ( initialized == false)
|
||||
{
|
||||
gettimeofday( &tp, 0 );
|
||||
initialized=true;
|
||||
// I do this because otherwise RakNet::Time in milliseconds won't work as it will underflow when dividing by 1000 to do the conversion
|
||||
initialTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec );
|
||||
}
|
||||
|
||||
// GCC
|
||||
RakNet::TimeUS curTime;
|
||||
gettimeofday( &tp, 0 );
|
||||
|
||||
curTime = ( tp.tv_sec ) * (RakNet::TimeUS) 1000000 + ( tp.tv_usec );
|
||||
|
||||
#if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
return NormalizeTime(curTime - initialTime);
|
||||
#else
|
||||
return curTime - initialTime;
|
||||
#endif // #if defined(GET_TIME_SPIKE_LIMIT) && GET_TIME_SPIKE_LIMIT>0
|
||||
}
|
||||
#endif
|
||||
|
||||
RakNet::TimeUS RakNet::GetTimeUS( void )
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
return GetTimeUS_Windows();
|
||||
#else
|
||||
return GetTimeUS_Linux();
|
||||
#endif
|
||||
}
|
||||
bool RakNet::GreaterThan(RakNet::Time a, RakNet::Time b)
|
||||
{
|
||||
// a > b?
|
||||
const RakNet::Time halfSpan =(RakNet::Time) (((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2);
|
||||
return b!=a && b-a>halfSpan;
|
||||
}
|
||||
bool RakNet::LessThan(RakNet::Time a, RakNet::Time b)
|
||||
{
|
||||
// a < b?
|
||||
const RakNet::Time halfSpan = ((RakNet::Time)(const RakNet::Time)-1)/(RakNet::Time)2;
|
||||
return b!=a && b-a<halfSpan;
|
||||
}
|
||||
35
third-party/RakNet/src/RakNet/GetTime.hpp
vendored
Normal file
35
third-party/RakNet/src/RakNet/GetTime.hpp
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
/// \file GetTime.h
|
||||
/// \brief Returns the value from QueryPerformanceCounter. This is the function RakNet uses to represent time. This time won't match the time returned by GetTimeCount(). See http://www.jenkinssoftware.com/forum/index.php?topic=2798.0
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __GET_TIME_H
|
||||
#define __GET_TIME_H
|
||||
|
||||
#include "Export.hpp"
|
||||
#include "RakNetTime.hpp" // For RakNet::TimeMS
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Same as GetTimeMS
|
||||
/// Holds the time in either a 32 or 64 bit variable, depending on __GET_TIME_64BIT
|
||||
RakNet::Time RAK_DLL_EXPORT GetTime( void );
|
||||
|
||||
/// Return the time as 32 bit
|
||||
/// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp.
|
||||
RakNet::TimeMS RAK_DLL_EXPORT GetTimeMS( void );
|
||||
|
||||
/// Return the time as 64 bit
|
||||
/// \note The maximum delta between returned calls is 1 second - however, RakNet calls this constantly anyway. See NormalizeTime() in the cpp.
|
||||
RakNet::TimeUS RAK_DLL_EXPORT GetTimeUS( void );
|
||||
|
||||
/// a > b?
|
||||
extern RAK_DLL_EXPORT bool GreaterThan(RakNet::Time a, RakNet::Time b);
|
||||
/// a < b?
|
||||
extern RAK_DLL_EXPORT bool LessThan(RakNet::Time a, RakNet::Time b);
|
||||
}
|
||||
|
||||
#endif
|
||||
27
third-party/RakNet/src/RakNet/Getche.cpp
vendored
Normal file
27
third-party/RakNet/src/RakNet/Getche.cpp
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <conio.h> /* getche() */
|
||||
#elif defined(__S3E__)
|
||||
|
||||
#else
|
||||
|
||||
#include "Getche.hpp"
|
||||
|
||||
char getche()
|
||||
{
|
||||
|
||||
|
||||
struct termios oldt,
|
||||
newt;
|
||||
char ch;
|
||||
tcgetattr( STDIN_FILENO, &oldt );
|
||||
newt = oldt;
|
||||
newt.c_lflag &= ~( ICANON | ECHO );
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &newt );
|
||||
ch = getchar();
|
||||
tcsetattr( STDIN_FILENO, TCSANOW, &oldt );
|
||||
return ch;
|
||||
|
||||
}
|
||||
#endif
|
||||
11
third-party/RakNet/src/RakNet/Getche.hpp
vendored
Normal file
11
third-party/RakNet/src/RakNet/Getche.hpp
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <conio.h> /* getche() */
|
||||
|
||||
#else
|
||||
#include <termios.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
char getche();
|
||||
#endif
|
||||
25
third-party/RakNet/src/RakNet/Gets.cpp
vendored
Normal file
25
third-party/RakNet/src/RakNet/Gets.cpp
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char * Gets ( char * str, int num )
|
||||
{
|
||||
fgets(str, num, stdin);
|
||||
if (str[0]=='\n' || str[0]=='\r')
|
||||
str[0]=0;
|
||||
|
||||
size_t len=strlen(str);
|
||||
if (len>0 && (str[len-1]=='\n' || str[len-1]=='\r'))
|
||||
str[len-1]=0;
|
||||
if (len>1 && (str[len-2]=='\n' || str[len-2]=='\r'))
|
||||
str[len-2]=0;
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
14
third-party/RakNet/src/RakNet/Gets.hpp
vendored
Normal file
14
third-party/RakNet/src/RakNet/Gets.hpp
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef __GETS__H_
|
||||
#define __GETS__H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char * Gets ( char * str, int num );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
191
third-party/RakNet/src/RakNet/GridSectorizer.cpp
vendored
Normal file
191
third-party/RakNet/src/RakNet/GridSectorizer.cpp
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
#include "RakAssert.hpp"
|
||||
#include "GridSectorizer.hpp"
|
||||
//#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
GridSectorizer::GridSectorizer()
|
||||
{
|
||||
grid=0;
|
||||
}
|
||||
GridSectorizer::~GridSectorizer()
|
||||
{
|
||||
if (grid)
|
||||
RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_);
|
||||
}
|
||||
void GridSectorizer::Init(const float _maxCellWidth, const float _maxCellHeight, const float minX, const float minY, const float maxX, const float maxY)
|
||||
{
|
||||
RakAssert(_maxCellWidth > 0.0f && _maxCellHeight > 0.0f);
|
||||
if (grid)
|
||||
RakNet::OP_DELETE_ARRAY(grid, _FILE_AND_LINE_);
|
||||
|
||||
cellOriginX=minX;
|
||||
cellOriginY=minY;
|
||||
gridWidth=maxX-minX;
|
||||
gridHeight=maxY-minY;
|
||||
gridCellWidthCount=(int) ceil(gridWidth/_maxCellWidth);
|
||||
gridCellHeightCount=(int) ceil(gridHeight/_maxCellHeight);
|
||||
// Make the cells slightly smaller, so we allocate an extra unneeded cell if on the edge. This way we don't go outside the array on rounding errors.
|
||||
cellWidth=gridWidth/gridCellWidthCount;
|
||||
cellHeight=gridHeight/gridCellHeightCount;
|
||||
invCellWidth = 1.0f / cellWidth;
|
||||
invCellHeight = 1.0f / cellHeight;
|
||||
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
grid = RakNet::OP_NEW<DataStructures::OrderedList<void*, void*>>(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ );
|
||||
DataStructures::OrderedList<void*,void*>::IMPLEMENT_DEFAULT_COMPARISON();
|
||||
#else
|
||||
grid = RakNet::OP_NEW_ARRAY<DataStructures::List<void*> >(gridCellWidthCount*gridCellHeightCount, _FILE_AND_LINE_ );
|
||||
#endif
|
||||
}
|
||||
void GridSectorizer::AddEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY)
|
||||
{
|
||||
RakAssert(cellWidth>0.0f);
|
||||
RakAssert(minX < maxX && minY < maxY);
|
||||
|
||||
int xStart, yStart, xEnd, yEnd, xCur, yCur;
|
||||
xStart=WorldToCellXOffsetAndClamped(minX);
|
||||
yStart=WorldToCellYOffsetAndClamped(minY);
|
||||
xEnd=WorldToCellXOffsetAndClamped(maxX);
|
||||
yEnd=WorldToCellYOffsetAndClamped(maxY);
|
||||
|
||||
for (xCur=xStart; xCur <= xEnd; ++xCur)
|
||||
{
|
||||
for (yCur=yStart; yCur <= yEnd; ++yCur)
|
||||
{
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true);
|
||||
#else
|
||||
grid[yCur*gridCellWidthCount+xCur].Insert(entry, _FILE_AND_LINE_);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
void GridSectorizer::RemoveEntry(void *entry, const float minX, const float minY, const float maxX, const float maxY)
|
||||
{
|
||||
RakAssert(cellWidth>0.0f);
|
||||
RakAssert(minX <= maxX && minY <= maxY);
|
||||
|
||||
int xStart, yStart, xEnd, yEnd, xCur, yCur;
|
||||
xStart=WorldToCellXOffsetAndClamped(minX);
|
||||
yStart=WorldToCellYOffsetAndClamped(minY);
|
||||
xEnd=WorldToCellXOffsetAndClamped(maxX);
|
||||
yEnd=WorldToCellYOffsetAndClamped(maxY);
|
||||
|
||||
for (xCur=xStart; xCur <= xEnd; ++xCur)
|
||||
{
|
||||
for (yCur=yStart; yCur <= yEnd; ++yCur)
|
||||
{
|
||||
grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
void GridSectorizer::MoveEntry(void *entry, const float sourceMinX, const float sourceMinY, const float sourceMaxX, const float sourceMaxY,
|
||||
const float destMinX, const float destMinY, const float destMaxX, const float destMaxY)
|
||||
{
|
||||
RakAssert(cellWidth>0.0f);
|
||||
RakAssert(sourceMinX < sourceMaxX && sourceMinY < sourceMaxY);
|
||||
RakAssert(destMinX < destMaxX && destMinY < destMaxY);
|
||||
|
||||
if (PositionCrossesCells(sourceMinX, sourceMinY, destMinX, destMinY)==false &&
|
||||
PositionCrossesCells(destMinX, destMinY, destMinX, destMinY)==false)
|
||||
return;
|
||||
|
||||
int xStartSource, yStartSource, xEndSource, yEndSource;
|
||||
int xStartDest, yStartDest, xEndDest, yEndDest;
|
||||
int xCur, yCur;
|
||||
xStartSource=WorldToCellXOffsetAndClamped(sourceMinX);
|
||||
yStartSource=WorldToCellYOffsetAndClamped(sourceMinY);
|
||||
xEndSource=WorldToCellXOffsetAndClamped(sourceMaxX);
|
||||
yEndSource=WorldToCellYOffsetAndClamped(sourceMaxY);
|
||||
|
||||
xStartDest=WorldToCellXOffsetAndClamped(destMinX);
|
||||
yStartDest=WorldToCellYOffsetAndClamped(destMinY);
|
||||
xEndDest=WorldToCellXOffsetAndClamped(destMaxX);
|
||||
yEndDest=WorldToCellYOffsetAndClamped(destMaxY);
|
||||
|
||||
// Remove source that is not in dest
|
||||
for (xCur=xStartSource; xCur <= xEndSource; ++xCur)
|
||||
{
|
||||
for (yCur=yStartSource; yCur <= yEndSource; ++yCur)
|
||||
{
|
||||
if (xCur < xStartDest || xCur > xEndDest ||
|
||||
yCur < yStartDest || yCur > yEndDest)
|
||||
{
|
||||
grid[yCur*gridCellWidthCount+xCur].RemoveIfExists(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add dest that is not in source
|
||||
for (xCur=xStartDest; xCur <= xEndDest; ++xCur)
|
||||
{
|
||||
for (yCur=yStartDest; yCur <= yEndDest; ++yCur)
|
||||
{
|
||||
if (xCur < xStartSource || xCur > xEndSource ||
|
||||
yCur < yStartSource || yCur > yEndSource)
|
||||
{
|
||||
grid[yCur*gridCellWidthCount+xCur].Insert(entry,entry, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void GridSectorizer::GetEntries(DataStructures::List<void*>& intersectionList, const float minX, const float minY, const float maxX, const float maxY)
|
||||
{
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
DataStructures::OrderedList<void*, void*>* cell;
|
||||
#else
|
||||
DataStructures::List<void*>* cell;
|
||||
#endif
|
||||
int xStart, yStart, xEnd, yEnd, xCur, yCur;
|
||||
unsigned index;
|
||||
xStart=WorldToCellXOffsetAndClamped(minX);
|
||||
yStart=WorldToCellYOffsetAndClamped(minY);
|
||||
xEnd=WorldToCellXOffsetAndClamped(maxX);
|
||||
yEnd=WorldToCellYOffsetAndClamped(maxY);
|
||||
|
||||
intersectionList.Clear(true, _FILE_AND_LINE_);
|
||||
for (xCur=xStart; xCur <= xEnd; ++xCur)
|
||||
{
|
||||
for (yCur=yStart; yCur <= yEnd; ++yCur)
|
||||
{
|
||||
cell = grid+yCur*gridCellWidthCount+xCur;
|
||||
for (index=0; index < cell->Size(); ++index)
|
||||
intersectionList.Insert(cell->operator [](index), _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool GridSectorizer::PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const
|
||||
{
|
||||
return originX/cellWidth!=destinationX/cellWidth || originY/cellHeight!=destinationY/cellHeight;
|
||||
}
|
||||
int GridSectorizer::WorldToCellX(const float input) const
|
||||
{
|
||||
return (int)((input-cellOriginX)*invCellWidth);
|
||||
}
|
||||
int GridSectorizer::WorldToCellY(const float input) const
|
||||
{
|
||||
return (int)((input-cellOriginY)*invCellHeight);
|
||||
}
|
||||
int GridSectorizer::WorldToCellXOffsetAndClamped(const float input) const
|
||||
{
|
||||
int cell=WorldToCellX(input);
|
||||
cell = cell > 0 ? cell : 0; // __max(cell,0);
|
||||
cell = gridCellWidthCount-1 < cell ? gridCellWidthCount-1 : cell; // __min(gridCellWidthCount-1, cell);
|
||||
return cell;
|
||||
}
|
||||
int GridSectorizer::WorldToCellYOffsetAndClamped(const float input) const
|
||||
{
|
||||
int cell=WorldToCellY(input);
|
||||
cell = cell > 0 ? cell : 0; // __max(cell,0);
|
||||
cell = gridCellHeightCount-1 < cell ? gridCellHeightCount-1 : cell; // __min(gridCellHeightCount-1, cell);
|
||||
return cell;
|
||||
}
|
||||
void GridSectorizer::Clear(void)
|
||||
{
|
||||
int cur;
|
||||
int count = gridCellWidthCount*gridCellHeightCount;
|
||||
for (cur=0; cur<count;cur++)
|
||||
grid[cur].Clear(true, _FILE_AND_LINE_);
|
||||
}
|
||||
68
third-party/RakNet/src/RakNet/GridSectorizer.hpp
vendored
Normal file
68
third-party/RakNet/src/RakNet/GridSectorizer.hpp
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
#ifndef _GRID_SECTORIZER_H
|
||||
#define _GRID_SECTORIZER_H
|
||||
|
||||
//#define _USE_ORDERED_LIST
|
||||
|
||||
#include "RakMemoryOverride.hpp"
|
||||
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
#include "DS_OrderedList.hpp"
|
||||
#else
|
||||
#include "DS_List.hpp"
|
||||
#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<void*>& intersectionList, const float minX, const float minY, const float maxX, const float maxY);
|
||||
|
||||
void Clear(void);
|
||||
|
||||
protected:
|
||||
int WorldToCellX(const float input) const;
|
||||
int WorldToCellY(const float input) const;
|
||||
int WorldToCellXOffsetAndClamped(const float input) const;
|
||||
int WorldToCellYOffsetAndClamped(const float input) const;
|
||||
|
||||
// Returns true or false if a position crosses cells in the grid. If false, you don't need to move entries
|
||||
bool PositionCrossesCells(const float originX, const float originY, const float destinationX, const float destinationY) const;
|
||||
|
||||
float cellOriginX, cellOriginY;
|
||||
float cellWidth, cellHeight;
|
||||
float invCellWidth, invCellHeight;
|
||||
float gridWidth, gridHeight;
|
||||
int gridCellWidthCount, gridCellHeightCount;
|
||||
|
||||
|
||||
// int gridWidth, gridHeight;
|
||||
|
||||
#ifdef _USE_ORDERED_LIST
|
||||
DataStructures::OrderedList<void*, void*>* grid;
|
||||
#else
|
||||
DataStructures::List<void*>* grid;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
313
third-party/RakNet/src/RakNet/HTTPConnection.cpp
vendored
Normal file
313
third-party/RakNet/src/RakNet/HTTPConnection.cpp
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
/// \file
|
||||
/// \brief Contains HTTPConnection, used to communicate with web servers
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2008 Kevin Jenkins.
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
/// Creative Commons Licensees are subject to the
|
||||
/// license found at
|
||||
/// http://creativecommons.org/licenses/by-nc/2.5/
|
||||
/// Single application licensees are subject to the license found at
|
||||
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
|
||||
/// Custom license users are subject to the terms therein.
|
||||
/// GPL license users are subject to the GNU General Public
|
||||
/// License as published by the Free
|
||||
/// Software Foundation; either version 2 of the License, or (at your
|
||||
/// option) any later version.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
|
||||
#include "TCPInterface.hpp"
|
||||
#include "HTTPConnection.hpp"
|
||||
#include "RakSleep.hpp"
|
||||
#include "RakString.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(HTTPConnection,HTTPConnection);
|
||||
|
||||
HTTPConnection::HTTPConnection() : connectionState(CS_NONE)
|
||||
{
|
||||
tcp=0;
|
||||
}
|
||||
|
||||
void HTTPConnection::Init(TCPInterface* _tcp, const char *_host, unsigned short _port)
|
||||
{
|
||||
tcp=_tcp;
|
||||
host=_host;
|
||||
port=_port;
|
||||
}
|
||||
|
||||
void HTTPConnection::Post(const char *remote_path, const char *data, const char *_contentType)
|
||||
{
|
||||
OutgoingCommand op;
|
||||
op.contentType=_contentType;
|
||||
op.data=data;
|
||||
op.remotePath=remote_path;
|
||||
op.isPost=true;
|
||||
outgoingCommand.Push(op, _FILE_AND_LINE_ );
|
||||
//printf("Adding outgoing post\n");
|
||||
}
|
||||
|
||||
void HTTPConnection::Get(const char *path)
|
||||
{
|
||||
OutgoingCommand op;
|
||||
op.remotePath=path;
|
||||
op.isPost=false;
|
||||
outgoingCommand.Push(op, _FILE_AND_LINE_ );
|
||||
}
|
||||
|
||||
bool HTTPConnection::HasBadResponse(int *code, RakNet::RakString *data)
|
||||
{
|
||||
if(badResponses.IsEmpty())
|
||||
return false;
|
||||
|
||||
if (code)
|
||||
*code = badResponses.Peek().code;
|
||||
if (data)
|
||||
*data = badResponses.Pop().data;
|
||||
return true;
|
||||
}
|
||||
void HTTPConnection::CloseConnection()
|
||||
{
|
||||
connectionState=CS_DISCONNECTING;
|
||||
}
|
||||
void HTTPConnection::Update(void)
|
||||
{
|
||||
SystemAddress sa;
|
||||
sa = tcp->HasCompletedConnectionAttempt();
|
||||
while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
// printf("Connected\n");
|
||||
connectionState=CS_CONNECTED;
|
||||
server=sa;
|
||||
sa = tcp->HasCompletedConnectionAttempt();
|
||||
}
|
||||
|
||||
sa = tcp->HasFailedConnectionAttempt();
|
||||
while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
//printf("Failed connected\n");
|
||||
CloseConnection();
|
||||
sa = tcp->HasFailedConnectionAttempt();
|
||||
}
|
||||
|
||||
sa = tcp->HasLostConnection();
|
||||
while (sa!=UNASSIGNED_SYSTEM_ADDRESS)
|
||||
{
|
||||
//printf("Lost connection\n");
|
||||
CloseConnection();
|
||||
sa = tcp->HasLostConnection();
|
||||
}
|
||||
|
||||
|
||||
switch (connectionState)
|
||||
{
|
||||
case CS_NONE:
|
||||
{
|
||||
if (outgoingCommand.IsEmpty())
|
||||
return;
|
||||
|
||||
//printf("Connecting\n");
|
||||
server = tcp->Connect(host, port, false);
|
||||
connectionState = CS_CONNECTING;
|
||||
}
|
||||
break;
|
||||
case CS_DISCONNECTING:
|
||||
{
|
||||
if (tcp->ReceiveHasPackets()==false)
|
||||
{
|
||||
if (incomingData.IsEmpty()==false)
|
||||
{
|
||||
results.Push(incomingData, _FILE_AND_LINE_ );
|
||||
}
|
||||
incomingData.Clear();
|
||||
tcp->CloseConnection(server);
|
||||
connectionState=CS_NONE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CS_CONNECTING:
|
||||
{
|
||||
}
|
||||
break;
|
||||
case CS_CONNECTED:
|
||||
{
|
||||
//printf("Connected\n");
|
||||
if (outgoingCommand.IsEmpty())
|
||||
{
|
||||
//printf("Closed connection (nothing to do)\n");
|
||||
CloseConnection();
|
||||
return;
|
||||
}
|
||||
|
||||
#if OPEN_SSL_CLIENT_SUPPORT==1
|
||||
tcp->StartSSLClient(server);
|
||||
#endif
|
||||
|
||||
//printf("Sending request\n");
|
||||
currentProcessingCommand = outgoingCommand.Pop();
|
||||
RakString request;
|
||||
if (currentProcessingCommand.isPost)
|
||||
{
|
||||
request.Set("POST %s HTTP/1.0\r\n"
|
||||
"Host: %s:%i\r\n"
|
||||
"Content-Type: %s\r\n"
|
||||
"Content-Length: %u\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
currentProcessingCommand.remotePath.C_String(),
|
||||
host.C_String(),
|
||||
port,
|
||||
currentProcessingCommand.contentType.C_String(),
|
||||
(unsigned) currentProcessingCommand.data.GetLength(),
|
||||
currentProcessingCommand.data.C_String());
|
||||
}
|
||||
else
|
||||
{
|
||||
// request.Set("GET %s\r\n", host.C_String());
|
||||
// http://www.jenkinssoftware.com/forum/index.php?topic=4601.0;topicseen
|
||||
request.Set("GET %s HTTP/1.0\r\n"
|
||||
"Host: %s:%i\r\n"
|
||||
"\r\n",
|
||||
currentProcessingCommand.remotePath.C_String(),
|
||||
host.C_String(),
|
||||
port);
|
||||
}
|
||||
|
||||
// printf(request.C_String());
|
||||
// request.URLEncode();
|
||||
tcp->Send(request.C_String(), (unsigned int) request.GetLength(), server,false);
|
||||
connectionState=CS_PROCESSING;
|
||||
}
|
||||
break;
|
||||
case CS_PROCESSING:
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
// if (connectionState==CS_PROCESSING && currentProcessingCommand.data.IsEmpty()==false)
|
||||
// outgoingCommand.PushAtHead(currentProcessingCommand);
|
||||
}
|
||||
bool HTTPConnection::HasRead(void) const
|
||||
{
|
||||
return results.IsEmpty()==false;
|
||||
}
|
||||
RakString HTTPConnection::Read(void)
|
||||
{
|
||||
if (results.IsEmpty())
|
||||
return RakString();
|
||||
|
||||
RakNet::RakString resultStr = results.Pop();
|
||||
// const char *start_of_body = strstr(resultStr.C_String(), "\r\n\r\n");
|
||||
const char *start_of_body = strpbrk(resultStr.C_String(), "\001\002\003%");
|
||||
|
||||
if(start_of_body)
|
||||
return RakNet::RakString::NonVariadic(start_of_body);
|
||||
else
|
||||
return resultStr;
|
||||
}
|
||||
SystemAddress HTTPConnection::GetServerAddress(void) const
|
||||
{
|
||||
return server;
|
||||
}
|
||||
void HTTPConnection::ProcessTCPPacket(Packet *packet)
|
||||
{
|
||||
RakAssert(packet);
|
||||
|
||||
// read all the packets possible
|
||||
if(packet->systemAddress == server)
|
||||
{
|
||||
if(incomingData.GetLength() == 0)
|
||||
{
|
||||
int response_code = atoi((char *)packet->data + strlen("HTTP/1.0 "));
|
||||
|
||||
if(response_code > 299)
|
||||
{
|
||||
badResponses.Push(BadResponse(packet->data, response_code), _FILE_AND_LINE_ );
|
||||
//printf("Closed connection (Bad response 2)\n");
|
||||
CloseConnection();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
RakNet::RakString incomingTemp = RakNet::RakString::NonVariadic((const char*) packet->data);
|
||||
incomingTemp.URLDecode();
|
||||
incomingData += incomingTemp;
|
||||
|
||||
// printf((const char*) packet->data);
|
||||
// printf("\n");
|
||||
|
||||
RakAssert(strlen((char *)packet->data) == packet->length); // otherwise it contains Null bytes
|
||||
|
||||
const char *start_of_body = strstr(incomingData, "\r\n\r\n");
|
||||
|
||||
// besides having the server close the connection, they may
|
||||
// provide a length header and supply that many bytes
|
||||
if(
|
||||
// Why was start_of_body here? Makes the GET command fail
|
||||
// start_of_body &&
|
||||
connectionState == CS_PROCESSING)
|
||||
{
|
||||
/*
|
||||
// The stupid programmer that wrote this originally didn't think that just because the header contains this value doesn't mean you got the whole message
|
||||
if (strstr((const char*) packet->data, "\r\nConnection: close\r\n"))
|
||||
{
|
||||
CloseConnection();
|
||||
}
|
||||
else
|
||||
{
|
||||
*/
|
||||
long length_of_headers;
|
||||
if (start_of_body)
|
||||
{
|
||||
length_of_headers = (long)(start_of_body + 4 - incomingData.C_String());
|
||||
const char *length_header = strstr(incomingData, "\r\nLength: ");
|
||||
|
||||
if(length_header)
|
||||
{
|
||||
long length = atol(length_header + 10) + length_of_headers;
|
||||
|
||||
if((long) incomingData.GetLength() >= length)
|
||||
{
|
||||
//printf("Closed connection (Got all data due to length header)\n");
|
||||
CloseConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No processing needed
|
||||
}
|
||||
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool HTTPConnection::IsBusy(void) const
|
||||
{
|
||||
return connectionState != CS_NONE;
|
||||
}
|
||||
|
||||
int HTTPConnection::GetState(void) const
|
||||
{
|
||||
return connectionState;
|
||||
}
|
||||
|
||||
|
||||
HTTPConnection::~HTTPConnection(void)
|
||||
{
|
||||
if (tcp)
|
||||
tcp->CloseConnection(server);
|
||||
}
|
||||
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
176
third-party/RakNet/src/RakNet/HTTPConnection.hpp
vendored
Normal file
176
third-party/RakNet/src/RakNet/HTTPConnection.hpp
vendored
Normal file
@@ -0,0 +1,176 @@
|
||||
/// \file HTTPConnection.h
|
||||
/// \brief Contains HTTPConnection, used to communicate with web servers
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2008 Kevin Jenkins.
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
/// Creative Commons Licensees are subject to the
|
||||
/// license found at
|
||||
/// http://creativecommons.org/licenses/by-nc/2.5/
|
||||
/// Single application licensees are subject to the license found at
|
||||
/// http://www.jenkinssoftware.com/SingleApplicationLicense.html
|
||||
/// Custom license users are subject to the terms therein.
|
||||
/// GPL license users are subject to the GNU General Public
|
||||
/// License as published by the Free
|
||||
/// Software Foundation; either version 2 of the License, or (at your
|
||||
/// option) any later version.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_HTTPConnection==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
|
||||
#ifndef __HTTP_CONNECTION
|
||||
#define __HTTP_CONNECTION
|
||||
|
||||
#include "Export.hpp"
|
||||
#include "RakString.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "DS_Queue.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class TCPInterface;
|
||||
struct SystemAddress;
|
||||
|
||||
/// \brief Use HTTPConnection to communicate with a web server.
|
||||
/// \details 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:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(HTTPConnection)
|
||||
|
||||
/// Returns a HTTP object associated with this tcp connection
|
||||
HTTPConnection();
|
||||
virtual ~HTTPConnection();
|
||||
|
||||
/// \pre tcp should already be started
|
||||
void Init(TCPInterface *_tcp, const char *host, unsigned short port=80);
|
||||
|
||||
/// 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 "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 a file from a webserver
|
||||
/// \param path the path on the remote server you want to GET from. For example "index.html"
|
||||
void Get(const char *path);
|
||||
|
||||
/// Is there a Read result ready?
|
||||
bool HasRead(void) const;
|
||||
|
||||
/// Get one result from the server
|
||||
/// \pre HasResult must return true
|
||||
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
|
||||
void ProcessTCPPacket(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;
|
||||
|
||||
struct OutgoingCommand
|
||||
{
|
||||
RakNet::RakString remotePath;
|
||||
RakNet::RakString data;
|
||||
RakNet::RakString contentType;
|
||||
bool isPost;
|
||||
};
|
||||
|
||||
DataStructures::Queue<OutgoingCommand> outgoingCommand;
|
||||
OutgoingCommand currentProcessingCommand;
|
||||
|
||||
private:
|
||||
SystemAddress server;
|
||||
TCPInterface *tcp;
|
||||
RakNet::RakString host;
|
||||
unsigned short port;
|
||||
DataStructures::Queue<BadResponse> badResponses;
|
||||
|
||||
enum ConnectionState
|
||||
{
|
||||
CS_NONE,
|
||||
CS_DISCONNECTING,
|
||||
CS_CONNECTING,
|
||||
CS_CONNECTED,
|
||||
CS_PROCESSING,
|
||||
} connectionState;
|
||||
|
||||
RakNet::RakString incomingData;
|
||||
DataStructures::Queue<RakNet::RakString> results;
|
||||
|
||||
void CloseConnection();
|
||||
|
||||
/*
|
||||
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;
|
||||
void Process(Packet *packet); // the workhorse
|
||||
|
||||
// this helps check the various status lists in TCPInterface
|
||||
typedef SystemAddress (TCPInterface::*StatusCheckFunction)(void);
|
||||
bool InList(StatusCheckFunction func);
|
||||
*/
|
||||
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
15
third-party/RakNet/src/RakNet/IncrementalReadInterface.cpp
vendored
Normal file
15
third-party/RakNet/src/RakNet/IncrementalReadInterface.cpp
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "IncrementalReadInterface.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
unsigned int IncrementalReadInterface::GetFilePart( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context)
|
||||
{
|
||||
FILE *fp = fopen(filename, "rb");
|
||||
if (fp==0)
|
||||
return 0;
|
||||
fseek(fp,startReadBytes,SEEK_SET);
|
||||
unsigned int numRead = (unsigned int) fread(preallocatedDestination,1,numBytesToRead, fp);
|
||||
fclose(fp);
|
||||
return numRead;
|
||||
}
|
||||
28
third-party/RakNet/src/RakNet/IncrementalReadInterface.hpp
vendored
Normal file
28
third-party/RakNet/src/RakNet/IncrementalReadInterface.hpp
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef __INCREMENTAL_READ_INTERFACE_H
|
||||
#define __INCREMENTAL_READ_INTERFACE_H
|
||||
|
||||
#include "FileListNodeContext.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
class RAK_DLL_EXPORT IncrementalReadInterface
|
||||
{
|
||||
public:
|
||||
IncrementalReadInterface() {}
|
||||
virtual ~IncrementalReadInterface() {}
|
||||
|
||||
/// 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( const char *filename, unsigned int startReadBytes, unsigned int numBytesToRead, void *preallocatedDestination, FileListNodeContext context);
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
131
third-party/RakNet/src/RakNet/InternalPacket.hpp
vendored
Normal file
131
third-party/RakNet/src/RakNet/InternalPacket.hpp
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
/// \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 Jenkins Software LLC
|
||||
///
|
||||
/// 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.hpp"
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "RakMemoryOverride.hpp"
|
||||
#include "RakNetDefines.hpp"
|
||||
#include "NativeTypes.hpp"
|
||||
#include "RakNetDefines.hpp"
|
||||
#if USE_SLIDING_WINDOW_CONGESTION_CONTROL!=1
|
||||
#include "CCRakNetUDT.hpp"
|
||||
#else
|
||||
#include "CCRakNetSlidingWindow.hpp"
|
||||
#endif
|
||||
|
||||
namespace RakNet {
|
||||
|
||||
typedef uint16_t SplitPacketIdType;
|
||||
typedef uint32_t 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
|
||||
/// Internally assumed to be 4 bytes, but written as 3 bytes in ReliabilityLayer::WriteToBitStreamFromInternalPacket
|
||||
typedef uint24_t 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 RakNet::TimeUS RemoteSystemTimeType;
|
||||
|
||||
struct InternalPacketFixedSizeTransmissionHeader
|
||||
{
|
||||
/// A unique numerical identifier given to this user message. Used to identify reliable messages on the network
|
||||
MessageNumberType reliableMessageNumber;
|
||||
///The ID used as identification for ordering messages. Also included in sequenced messages
|
||||
OrderingIndexType orderingIndex;
|
||||
// Used only with sequenced messages
|
||||
OrderingIndexType sequencingIndex;
|
||||
///What ordering channel this packet is on, if the reliability type uses ordering channels
|
||||
unsigned char orderingChannel;
|
||||
///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;;
|
||||
///How many bits long the data is
|
||||
BitSize_t dataBitLength;
|
||||
///What type of reliability algorithm to use with this packet
|
||||
PacketReliability reliability;
|
||||
// Not endian safe
|
||||
// unsigned char priority : 3;
|
||||
// unsigned char reliability : 5;
|
||||
};
|
||||
|
||||
/// Used in InternalPacket when pointing to sharedDataBlock, rather than allocating itself
|
||||
struct InternalPacketRefCountedData
|
||||
{
|
||||
unsigned char *sharedDataBlock;
|
||||
unsigned int refCount;
|
||||
};
|
||||
|
||||
/// Holds a user message, and related information
|
||||
/// Don't use a constructor or destructor, due to the memory pool I am using
|
||||
struct InternalPacket : public InternalPacketFixedSizeTransmissionHeader
|
||||
{
|
||||
/// 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;
|
||||
/// Was this packet number used this update to track windowing drops or increases? Each packet number is only used once per update.
|
||||
// bool allowWindowUpdate;
|
||||
///When this packet was created
|
||||
RakNet::TimeUS creationTime;
|
||||
///The resendNext time to take action on this packet
|
||||
RakNet::TimeUS nextActionTime;
|
||||
// Size of the header when encoded into a bitstream
|
||||
BitSize_t headerLength;
|
||||
/// Buffer is a pointer to the actual data, assuming this packet has data at all
|
||||
unsigned char *data;
|
||||
/// How to alloc and delete the data member
|
||||
enum AllocationScheme
|
||||
{
|
||||
/// Data is allocated using rakMalloc. Just free it
|
||||
NORMAL,
|
||||
|
||||
/// data points to a larger block of data, where the larger block is reference counted. internalPacketRefCountedData is used in this case
|
||||
REF_COUNTED,
|
||||
|
||||
/// If allocation scheme is STACK, data points to stackData and should not be deallocated
|
||||
/// This is only used when sending. Received packets are deallocated in RakPeer
|
||||
STACK
|
||||
} allocationScheme;
|
||||
InternalPacketRefCountedData *refCountedData;
|
||||
/// How many attempts we made at sending this message
|
||||
// unsigned char timesSent;
|
||||
/// The priority level of this packet
|
||||
PacketPriority priority;
|
||||
/// If the reliability type requires a receipt, then return this number with it
|
||||
uint32_t sendReceiptSerial;
|
||||
|
||||
// Used for the resend queue
|
||||
// Linked list implementation so I can remove from the list via a pointer, without finding it in the list
|
||||
InternalPacket *resendPrev, *resendNext,*unreliablePrev,*unreliableNext;
|
||||
|
||||
unsigned char stackData[128];
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
52
third-party/RakNet/src/RakNet/Itoa.cpp
vendored
Normal file
52
third-party/RakNet/src/RakNet/Itoa.cpp
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Fast itoa from http://www.jb.man.ac.uk/~slowe/cpp/itoa.html for Linux since it seems like Linux doesn't support this function.
|
||||
// I modified it to remove the std dependencies.
|
||||
char* Itoa( int value, char* result, int base )
|
||||
{
|
||||
// check that the base if valid
|
||||
if (base < 2 || base > 16) { *result = 0; return result; }
|
||||
char* out = result;
|
||||
int quotient = value;
|
||||
|
||||
int absQModB;
|
||||
|
||||
do {
|
||||
// KevinJ - get rid of this dependency
|
||||
//*out = "0123456789abcdef"[ std::abs( quotient % base ) ];
|
||||
absQModB=quotient % base;
|
||||
if (absQModB < 0)
|
||||
absQModB=-absQModB;
|
||||
*out = "0123456789abcdef"[ absQModB ];
|
||||
++out;
|
||||
quotient /= base;
|
||||
} while ( quotient );
|
||||
|
||||
// Only apply negative sign for base 10
|
||||
if ( value < 0 && base == 10) *out++ = '-';
|
||||
|
||||
// KevinJ - get rid of this dependency
|
||||
// std::reverse( result, out );
|
||||
*out = 0;
|
||||
|
||||
// KevinJ - My own reverse code
|
||||
char *start = result;
|
||||
char temp;
|
||||
out--;
|
||||
while (start < out)
|
||||
{
|
||||
temp=*start;
|
||||
*start=*out;
|
||||
*out=temp;
|
||||
start++;
|
||||
out--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
15
third-party/RakNet/src/RakNet/Itoa.hpp
vendored
Normal file
15
third-party/RakNet/src/RakNet/Itoa.hpp
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __RAK_ITOA_H
|
||||
#define __RAK_ITOA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
char* Itoa( int value, char* result, int base );
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
84
third-party/RakNet/src/RakNet/Kbhit.hpp
vendored
Normal file
84
third-party/RakNet/src/RakNet/Kbhit.hpp
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/*****************************************************************************
|
||||
kbhit() and getch() for Linux/UNIX
|
||||
Chris Giese <geezer@execpc.com> http://my.execpc.com/~geezer
|
||||
Release date: ?
|
||||
This code is public domain (no copyright).
|
||||
You can do whatever you want with it.
|
||||
*****************************************************************************/
|
||||
#if defined(_WIN32)
|
||||
#include <conio.h> /* kbhit(), getch() */
|
||||
|
||||
#else
|
||||
#include <sys/time.h> /* struct timeval, select() */
|
||||
/* ICANON, ECHO, TCSANOW, struct termios */
|
||||
#include <termios.h> /* tcgetattr(), tcsetattr() */
|
||||
#include <stdlib.h> /* atexit(), exit() */
|
||||
#include <unistd.h> /* read() */
|
||||
#include <stdio.h> /* printf() */
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
static struct termios g_old_kbd_mode;
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
static void cooked(void)
|
||||
{
|
||||
tcsetattr(0, TCSANOW, &g_old_kbd_mode);
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
static void raw(void)
|
||||
{
|
||||
static char init;
|
||||
/**/
|
||||
struct termios new_kbd_mode;
|
||||
|
||||
if(init)
|
||||
return;
|
||||
/* put keyboard (stdin, actually) in raw, unbuffered mode */
|
||||
tcgetattr(0, &g_old_kbd_mode);
|
||||
memcpy(&new_kbd_mode, &g_old_kbd_mode, sizeof(struct termios));
|
||||
new_kbd_mode.c_lflag &= ~(ICANON /*| ECHO */ );
|
||||
new_kbd_mode.c_cc[VTIME] = 0;
|
||||
new_kbd_mode.c_cc[VMIN] = 1;
|
||||
tcsetattr(0, TCSANOW, &new_kbd_mode);
|
||||
/* when we exit, go back to normal, "cooked" mode */
|
||||
atexit(cooked);
|
||||
|
||||
init = 1;
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
static int kbhit(void)
|
||||
{
|
||||
struct timeval timeout;
|
||||
fd_set read_handles;
|
||||
int status;
|
||||
|
||||
raw();
|
||||
/* check stdin (fd 0) for activity */
|
||||
FD_ZERO(&read_handles);
|
||||
FD_SET(0, &read_handles);
|
||||
timeout.tv_sec = timeout.tv_usec = 0;
|
||||
status = select(0 + 1, &read_handles, NULL, NULL, &timeout);
|
||||
if(status < 0)
|
||||
{
|
||||
printf("select() failed in kbhit()\n");
|
||||
exit(1);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
/*****************************************************************************
|
||||
*****************************************************************************/
|
||||
static int getch(void)
|
||||
{
|
||||
unsigned char temp;
|
||||
|
||||
raw();
|
||||
/* stdin = fd 0 */
|
||||
if(read(0, &temp, 1) != 1)
|
||||
return 0;
|
||||
return temp;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
26
third-party/RakNet/src/RakNet/LinuxStrings.cpp
vendored
Normal file
26
third-party/RakNet/src/RakNet/LinuxStrings.cpp
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#if (defined(__GNUC__) || defined(__ARMCC_VERSION) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32)
|
||||
#include <string.h>
|
||||
#ifndef _stricmp
|
||||
int _stricmp(const char* s1, const char* s2)
|
||||
{
|
||||
return strcasecmp(s1,s2);
|
||||
}
|
||||
#endif
|
||||
int _strnicmp(const char* s1, const char* s2, size_t n)
|
||||
{
|
||||
return strncasecmp(s1,s2,n);
|
||||
}
|
||||
#ifndef __APPLE__
|
||||
char *_strlwr(char * str )
|
||||
{
|
||||
if (str==0)
|
||||
return 0;
|
||||
for (int i=0; str[i]; i++)
|
||||
{
|
||||
if (str[i]>='A' && str[i]<='Z')
|
||||
str[i]+='a'-'A';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
26
third-party/RakNet/src/RakNet/LinuxStrings.hpp
vendored
Normal file
26
third-party/RakNet/src/RakNet/LinuxStrings.hpp
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef _GCC_WIN_STRINGS
|
||||
#define _GCC_WIN_STRINGS
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if (defined(__GNUC__) || defined(__GCCXML__) || defined(__S3E__)) && !defined(_WIN32)
|
||||
#ifndef _stricmp
|
||||
int _stricmp(const char* s1, const char* s2);
|
||||
#endif
|
||||
int _strnicmp(const char* s1, const char* s2, size_t n);
|
||||
#ifndef __APPLE__
|
||||
char *_strlwr(char * str ); //this won't compile on OSX for some reason
|
||||
#endif
|
||||
#ifndef _vsnprintf
|
||||
#define _vsnprintf vsnprintf
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif // _GCC_WIN_STRINGS
|
||||
42
third-party/RakNet/src/RakNet/LocklessTypes.cpp
vendored
Normal file
42
third-party/RakNet/src/RakNet/LocklessTypes.cpp
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
#include "LocklessTypes.hpp"
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
LocklessUint32_t::LocklessUint32_t()
|
||||
{
|
||||
value=0;
|
||||
}
|
||||
LocklessUint32_t::LocklessUint32_t(uint32_t initial)
|
||||
{
|
||||
value=initial;
|
||||
}
|
||||
uint32_t LocklessUint32_t::Increment(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (uint32_t) InterlockedIncrement(&value);
|
||||
#elif defined(ANDROID) || defined(__S3E__)
|
||||
uint32_t v;
|
||||
mutex.Lock();
|
||||
++value;
|
||||
v=value;
|
||||
mutex.Unlock();
|
||||
return v;
|
||||
#else
|
||||
return __sync_fetch_and_add (&value, (uint32_t) 1);
|
||||
#endif
|
||||
}
|
||||
uint32_t LocklessUint32_t::Decrement(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (uint32_t) InterlockedDecrement(&value);
|
||||
#elif defined(ANDROID) || defined(__S3E__)
|
||||
uint32_t v;
|
||||
mutex.Lock();
|
||||
--value;
|
||||
v=value;
|
||||
mutex.Unlock();
|
||||
return v;
|
||||
#else
|
||||
return __sync_fetch_and_add (&value, (uint32_t) -1);
|
||||
#endif
|
||||
}
|
||||
40
third-party/RakNet/src/RakNet/LocklessTypes.hpp
vendored
Normal file
40
third-party/RakNet/src/RakNet/LocklessTypes.hpp
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
#ifndef __LOCKLESS_TYPES_H
|
||||
#define __LOCKLESS_TYPES_H
|
||||
|
||||
#include "Export.hpp"
|
||||
#include "NativeTypes.hpp"
|
||||
#include "WindowsIncludes.hpp"
|
||||
#if defined(ANDROID) || defined(__S3E__)
|
||||
// __sync_fetch_and_add not supported apparently
|
||||
#include "SimpleMutex.hpp"
|
||||
#endif
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
|
||||
class RAK_DLL_EXPORT LocklessUint32_t
|
||||
{
|
||||
public:
|
||||
LocklessUint32_t();
|
||||
explicit LocklessUint32_t(uint32_t initial);
|
||||
// Returns variable value after changing it
|
||||
uint32_t Increment(void);
|
||||
// Returns variable value after changing it
|
||||
uint32_t Decrement(void);
|
||||
volatile uint32_t GetValue(void) const {return value;}
|
||||
|
||||
protected:
|
||||
#ifdef _WIN32
|
||||
volatile LONG value;
|
||||
#elif defined(ANDROID) || defined(__S3E__)
|
||||
// __sync_fetch_and_add not supported apparently
|
||||
SimpleMutex mutex;
|
||||
uint32_t value;
|
||||
#else
|
||||
volatile uint32_t value;
|
||||
#endif
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
279
third-party/RakNet/src/RakNet/LogCommandParser.cpp
vendored
Normal file
279
third-party/RakNet/src/RakNet/LogCommandParser.cpp
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_LogCommandParser==1
|
||||
|
||||
#include "LogCommandParser.hpp"
|
||||
#include "TransportInterface.hpp"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "LinuxStrings.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
STATIC_FACTORY_DEFINITIONS(LogCommandParser,LogCommandParser);
|
||||
|
||||
LogCommandParser::LogCommandParser()
|
||||
{
|
||||
RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Subscribe","[<ChannelName>] - Subscribes to a named channel, or all channels");
|
||||
RegisterCommand(CommandParserInterface::VARIABLE_NUMBER_OF_PARAMETERS,"Unsubscribe","[<ChannelName>] - Unsubscribes from a named channel, or all channels");
|
||||
memset(channelNames,0,sizeof(channelNames));
|
||||
}
|
||||
LogCommandParser::~LogCommandParser()
|
||||
{
|
||||
}
|
||||
bool LogCommandParser::OnCommand(const char *command, unsigned numParameters, char **parameterList, TransportInterface *transport, const SystemAddress &systemAddress, const char *originalString)
|
||||
{
|
||||
(void) originalString;
|
||||
|
||||
if (strcmp(command, "Subscribe")==0)
|
||||
{
|
||||
unsigned channelIndex;
|
||||
if (numParameters==0)
|
||||
{
|
||||
Subscribe(systemAddress, 0);
|
||||
transport->Send(systemAddress, "Subscribed to all channels.\r\n");
|
||||
}
|
||||
else if (numParameters==1)
|
||||
{
|
||||
if ((channelIndex=Subscribe(systemAddress, parameterList[0]))!=(unsigned)-1)
|
||||
{
|
||||
transport->Send(systemAddress, "You are now subscribed to channel %s.\r\n", channelNames[channelIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]);
|
||||
PrintChannels(systemAddress, transport);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transport->Send(systemAddress, "Subscribe takes either 0 or 1 parameters.\r\n");
|
||||
}
|
||||
}
|
||||
else if (strcmp(command, "Unsubscribe")==0)
|
||||
{
|
||||
unsigned channelIndex;
|
||||
if (numParameters==0)
|
||||
{
|
||||
Unsubscribe(systemAddress, 0);
|
||||
transport->Send(systemAddress, "Unsubscribed from all channels.\r\n");
|
||||
}
|
||||
else if (numParameters==1)
|
||||
{
|
||||
if ((channelIndex=Unsubscribe(systemAddress, parameterList[0]))!=(unsigned)-1)
|
||||
{
|
||||
transport->Send(systemAddress, "You are now unsubscribed from channel %s.\r\n", channelNames[channelIndex]);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport->Send(systemAddress, "Cannot find channel %s.\r\n", parameterList[0]);
|
||||
PrintChannels(systemAddress, transport);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transport->Send(systemAddress, "Unsubscribe takes either 0 or 1 parameters.\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
const char *LogCommandParser::GetName(void) const
|
||||
{
|
||||
return "Logger";
|
||||
}
|
||||
void LogCommandParser::SendHelp(TransportInterface *transport, const SystemAddress &systemAddress)
|
||||
{
|
||||
transport->Send(systemAddress, "The logger will accept user log data via the Log(...) function.\r\n");
|
||||
transport->Send(systemAddress, "Each log is associated with a named channel.\r\n");
|
||||
transport->Send(systemAddress, "You can subscribe to or unsubscribe from named channels.\r\n");
|
||||
PrintChannels(systemAddress, transport);
|
||||
}
|
||||
void LogCommandParser::AddChannel(const char *channelName)
|
||||
{
|
||||
unsigned channelIndex=0;
|
||||
channelIndex = GetChannelIndexFromName(channelName);
|
||||
// Each channel can only be added once.
|
||||
RakAssert(channelIndex==(unsigned)-1);
|
||||
|
||||
unsigned i;
|
||||
for (i=0; i < 32; i++)
|
||||
{
|
||||
if (channelNames[i]==0)
|
||||
{
|
||||
// Assuming a persistent static string.
|
||||
channelNames[i]=channelName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No more available channels - max 32 with this implementation where I save subscribed channels with bit operations
|
||||
RakAssert(0);
|
||||
}
|
||||
void LogCommandParser::WriteLog(const char *channelName, const char *format, ...)
|
||||
{
|
||||
if (channelName==0 || format==0)
|
||||
return;
|
||||
|
||||
unsigned channelIndex;
|
||||
channelIndex = GetChannelIndexFromName(channelName);
|
||||
if (channelIndex==(unsigned)-1)
|
||||
{
|
||||
AddChannel(channelName);
|
||||
}
|
||||
|
||||
char text[REMOTE_MAX_TEXT_INPUT];
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
_vsnprintf(text, REMOTE_MAX_TEXT_INPUT, format, ap);
|
||||
va_end(ap);
|
||||
text[REMOTE_MAX_TEXT_INPUT-1]=0;
|
||||
|
||||
// Make sure that text ends in \r\n
|
||||
int textLen;
|
||||
textLen=(int)strlen(text);
|
||||
if (textLen==0)
|
||||
return;
|
||||
if (text[textLen-1]=='\n')
|
||||
{
|
||||
text[textLen-1]=0;
|
||||
}
|
||||
if (textLen < REMOTE_MAX_TEXT_INPUT-4)
|
||||
strcat(text, "\r\n");
|
||||
else
|
||||
{
|
||||
text[textLen-3]='\r';
|
||||
text[textLen-2]='\n';
|
||||
text[textLen-1]=0;
|
||||
}
|
||||
|
||||
// For each user that subscribes to this channel, send to them.
|
||||
unsigned i;
|
||||
for (i=0; i < remoteUsers.Size(); i++)
|
||||
{
|
||||
if (remoteUsers[i].channels & (1 << channelIndex))
|
||||
{
|
||||
trans->Send(remoteUsers[i].systemAddress, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
void LogCommandParser::PrintChannels(const SystemAddress &systemAddress, TransportInterface *transport) const
|
||||
{
|
||||
unsigned i;
|
||||
bool anyChannels=false;
|
||||
transport->Send(systemAddress, "CHANNELS:\r\n");
|
||||
for (i=0; i < 32; i++)
|
||||
{
|
||||
if (channelNames[i])
|
||||
{
|
||||
transport->Send(systemAddress, "%i. %s\r\n", i+1,channelNames[i]);
|
||||
anyChannels=true;
|
||||
}
|
||||
}
|
||||
if (anyChannels==false)
|
||||
transport->Send(systemAddress, "None.\r\n");
|
||||
}
|
||||
void LogCommandParser::OnNewIncomingConnection(const SystemAddress &systemAddress, TransportInterface *transport)
|
||||
{
|
||||
(void) systemAddress;
|
||||
(void) transport;
|
||||
}
|
||||
void LogCommandParser::OnConnectionLost(const SystemAddress &systemAddress, TransportInterface *transport)
|
||||
{
|
||||
(void) transport;
|
||||
Unsubscribe(systemAddress, 0);
|
||||
}
|
||||
unsigned LogCommandParser::Unsubscribe(const SystemAddress &systemAddress, const char *channelName)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < remoteUsers.Size(); i++)
|
||||
{
|
||||
if (remoteUsers[i].systemAddress==systemAddress)
|
||||
{
|
||||
if (channelName==0)
|
||||
{
|
||||
// Unsubscribe from all and delete this user.
|
||||
remoteUsers[i]=remoteUsers[remoteUsers.Size()-1];
|
||||
remoteUsers.RemoveFromEnd();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned channelIndex;
|
||||
channelIndex = GetChannelIndexFromName(channelName);
|
||||
if (channelIndex!=(unsigned)-1)
|
||||
{
|
||||
remoteUsers[i].channels&=0xFFFF ^ (1<<channelIndex); // Unset this bit
|
||||
}
|
||||
return channelIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (unsigned)-1;
|
||||
}
|
||||
unsigned LogCommandParser::Subscribe(const SystemAddress &systemAddress, const char *channelName)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned channelIndex=(unsigned)-1;
|
||||
if (channelName)
|
||||
{
|
||||
channelIndex = GetChannelIndexFromName(channelName);
|
||||
if (channelIndex==(unsigned)-1)
|
||||
return channelIndex;
|
||||
}
|
||||
|
||||
for (i=0; i < remoteUsers.Size(); i++)
|
||||
{
|
||||
if (remoteUsers[i].systemAddress==systemAddress)
|
||||
{
|
||||
if (channelName)
|
||||
remoteUsers[i].channels|=1<<channelIndex; // Set this bit for an existing user
|
||||
else
|
||||
remoteUsers[i].channels=0xFFFF;
|
||||
return channelIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// Make a new user
|
||||
SystemAddressAndChannel newUser;
|
||||
newUser.systemAddress = systemAddress;
|
||||
if (channelName)
|
||||
newUser.channels=1<<channelIndex;
|
||||
else
|
||||
newUser.channels=0xFFFF;
|
||||
remoteUsers.Insert(newUser, _FILE_AND_LINE_);
|
||||
return channelIndex;
|
||||
}
|
||||
unsigned LogCommandParser::GetChannelIndexFromName(const char *channelName)
|
||||
{
|
||||
unsigned i;
|
||||
for (i=0; i < 32; i++)
|
||||
{
|
||||
if (channelNames[i]==0)
|
||||
return (unsigned) -1;
|
||||
|
||||
if (_stricmp(channelNames[i], channelName)==0)
|
||||
return i;
|
||||
}
|
||||
return (unsigned)-1;
|
||||
}
|
||||
|
||||
void LogCommandParser::OnTransportChange(TransportInterface *transport)
|
||||
{
|
||||
// I don't want users to have to pass TransportInterface *transport to Log.
|
||||
trans=transport;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
120
third-party/RakNet/src/RakNet/LogCommandParser.hpp
vendored
Normal file
120
third-party/RakNet/src/RakNet/LogCommandParser.hpp
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
/// \file
|
||||
/// \brief Contains LogCommandParser , Used to send logs to connected consoles
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_LogCommandParser==1
|
||||
|
||||
#ifndef __LOG_COMMAND_PARSER
|
||||
#define __LOG_COMMAND_PARSER
|
||||
|
||||
#include "CommandParserInterface.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
|
||||
/// \brief Adds the ability to send logging output to a remote console
|
||||
class RAK_DLL_EXPORT LogCommandParser : public CommandParserInterface
|
||||
{
|
||||
public:
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(LogCommandParser)
|
||||
|
||||
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, const 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, const 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(const 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(const 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(const 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(const 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(const SystemAddress &systemAddress, const char *channelName);
|
||||
|
||||
/// Given the name of a channel, return the index into channelNames where it is located
|
||||
/// \param[in] channelName The name of the channel
|
||||
unsigned GetChannelIndexFromName(const char *channelName);
|
||||
|
||||
/// One of these structures is created per player
|
||||
struct SystemAddressAndChannel
|
||||
{
|
||||
/// The ID of the player
|
||||
SystemAddress systemAddress;
|
||||
|
||||
/// Bitwise representations of the channels subscribed to. If bit 0 is set, then we subscribe to channelNames[0] and so on.
|
||||
unsigned channels;
|
||||
};
|
||||
|
||||
/// The list of remote users. Added to when users subscribe, removed when they disconnect or unsubscribe
|
||||
DataStructures::List<SystemAddressAndChannel> remoteUsers;
|
||||
|
||||
/// Names of the channels at each bit, or 0 for an unused channel
|
||||
const char *channelNames[32];
|
||||
|
||||
/// This is so I can save the current transport provider, solely so I can use it without having the user pass it to Log
|
||||
TransportInterface *trans;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
31
third-party/RakNet/src/RakNet/MTUSize.hpp
vendored
Normal file
31
third-party/RakNet/src/RakNet/MTUSize.hpp
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
/// \file
|
||||
/// \brief \b [Internal] Defines the default maximum transfer unit.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef MAXIMUM_MTU_SIZE
|
||||
|
||||
/// \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.
|
||||
/// The largest value for an UDP datagram
|
||||
|
||||
|
||||
|
||||
#define MAXIMUM_MTU_SIZE 1492
|
||||
|
||||
|
||||
#define MINIMUM_MTU_SIZE 400
|
||||
|
||||
#endif
|
||||
408
third-party/RakNet/src/RakNet/MessageFilter.cpp
vendored
Normal file
408
third-party/RakNet/src/RakNet/MessageFilter.cpp
vendored
Normal file
@@ -0,0 +1,408 @@
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_MessageFilter==1
|
||||
|
||||
#include "MessageFilter.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "GetTime.hpp"
|
||||
#include "MessageIdentifiers.hpp"
|
||||
#include "RakAssert.hpp"
|
||||
#include "RakPeerInterface.hpp"
|
||||
#include "PacketizedTCP.hpp"
|
||||
#include "BitStream.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( push )
|
||||
#endif
|
||||
|
||||
using namespace RakNet;
|
||||
|
||||
int RakNet::MessageFilterStrComp( char *const &key,char *const &data )
|
||||
{
|
||||
return strcmp(key,data);
|
||||
}
|
||||
|
||||
int RakNet::FilterSetComp( const int &key, FilterSet * const &data )
|
||||
{
|
||||
if (key < data->filterSetID)
|
||||
return -1;
|
||||
else if (key==data->filterSetID)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
STATIC_FACTORY_DEFINITIONS(MessageFilter,MessageFilter);
|
||||
|
||||
MessageFilter::MessageFilter()
|
||||
{
|
||||
whenLastTimeoutCheck=RakNet::GetTime();
|
||||
}
|
||||
MessageFilter::~MessageFilter()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
void MessageFilter::SetAutoAddNewConnectionsToFilter(int filterSetID)
|
||||
{
|
||||
autoAddNewConnectionsToFilter=filterSetID;
|
||||
}
|
||||
void MessageFilter::SetAllowMessageID(bool allow, int messageIDStart, int messageIDEnd,int filterSetID)
|
||||
{
|
||||
RakAssert(messageIDStart <= messageIDEnd);
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
int i;
|
||||
for (i=messageIDStart; i <= messageIDEnd; ++i)
|
||||
filterSet->allowedIDs[i]=allow;
|
||||
}
|
||||
void MessageFilter::SetAllowRPC4(bool allow, const char* uniqueID, int filterSetID)
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
bool objectExists;
|
||||
unsigned int idx = filterSet->allowedRPC4.GetIndexFromKey(uniqueID, &objectExists);
|
||||
if (allow)
|
||||
{
|
||||
if (objectExists==false)
|
||||
{
|
||||
filterSet->allowedRPC4.InsertAtIndex(uniqueID, idx, _FILE_AND_LINE_);
|
||||
filterSet->allowedIDs[ID_RPC_PLUGIN]=true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (objectExists==true)
|
||||
{
|
||||
filterSet->allowedRPC4.RemoveAtIndex(idx);
|
||||
if (filterSet->allowedRPC4.Size()==0)
|
||||
{
|
||||
filterSet->allowedIDs[ID_RPC_PLUGIN]=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void MessageFilter::SetActionOnDisallowedMessage(bool kickOnDisallowed, bool banOnDisallowed, RakNet::TimeMS banTimeMS, int filterSetID)
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
filterSet->kickOnDisallowedMessage=kickOnDisallowed;
|
||||
filterSet->disallowedMessageBanTimeMS=banTimeMS;
|
||||
filterSet->banOnDisallowedMessage=banOnDisallowed;
|
||||
}
|
||||
void MessageFilter::SetDisallowedMessageCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID))
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
filterSet->invalidMessageCallback=invalidMessageCallback;
|
||||
filterSet->disallowedCallbackUserData=userData;
|
||||
}
|
||||
void MessageFilter::SetTimeoutCallback(int filterSetID, void *userData, void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData))
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
filterSet->timeoutCallback=invalidMessageCallback;
|
||||
filterSet->timeoutUserData=userData;
|
||||
}
|
||||
void MessageFilter::SetFilterMaxTime(int allowedTimeMS, bool banOnExceed, RakNet::TimeMS banTimeMS, int filterSetID)
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
filterSet->maxMemberTimeMS=allowedTimeMS;
|
||||
filterSet->banOnFilterTimeExceed=banOnExceed;
|
||||
filterSet->timeExceedBanTimeMS=banTimeMS;
|
||||
}
|
||||
int MessageFilter::GetSystemFilterSet(AddressOrGUID systemAddress)
|
||||
{
|
||||
// bool objectExists;
|
||||
// unsigned index = systemList.GetIndexFromKey(systemAddress, &objectExists);
|
||||
// if (objectExists==false)
|
||||
// return -1;
|
||||
// else
|
||||
// return systemList[index].filter->filterSetID;
|
||||
|
||||
DataStructures::HashIndex index = systemList.GetIndexOf(systemAddress);
|
||||
if (index.IsInvalid())
|
||||
return -1;
|
||||
else
|
||||
return systemList.ItemAtIndex(index).filter->filterSetID;
|
||||
}
|
||||
void MessageFilter::SetSystemFilterSet(AddressOrGUID addressOrGUID, int filterSetID)
|
||||
{
|
||||
// Allocate this filter set if it doesn't exist.
|
||||
RakAssert(addressOrGUID.IsUndefined()==false);
|
||||
// bool objectExists;
|
||||
// unsigned index = systemList.GetIndexFromKey(addressOrGUID, &objectExists);
|
||||
// if (objectExists==false)
|
||||
DataStructures::HashIndex index = systemList.GetIndexOf(addressOrGUID);
|
||||
if (index.IsInvalid())
|
||||
{
|
||||
if (filterSetID<0)
|
||||
return;
|
||||
|
||||
FilteredSystem filteredSystem;
|
||||
filteredSystem.filter = GetFilterSetByID(filterSetID);
|
||||
// filteredSystem.addressOrGUID=addressOrGUID;
|
||||
filteredSystem.timeEnteredThisSet=RakNet::GetTimeMS();
|
||||
// systemList.Insert(addressOrGUID, filteredSystem, true, _FILE_AND_LINE_);
|
||||
systemList.Push(addressOrGUID,filteredSystem,_FILE_AND_LINE_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (filterSetID>=0)
|
||||
{
|
||||
FilterSet *filterSet = GetFilterSetByID(filterSetID);
|
||||
systemList.ItemAtIndex(index).timeEnteredThisSet=RakNet::GetTimeMS();
|
||||
systemList.ItemAtIndex(index).filter=filterSet;
|
||||
}
|
||||
else
|
||||
{
|
||||
systemList.RemoveAtIndex(index, _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned MessageFilter::GetSystemCount(int filterSetID) const
|
||||
{
|
||||
if (filterSetID==-1)
|
||||
{
|
||||
return systemList.Size();
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned i;
|
||||
unsigned count=0;
|
||||
DataStructures::List< FilteredSystem > itemList;
|
||||
DataStructures::List< AddressOrGUID > keyList;
|
||||
systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_);
|
||||
for (i=0; i < itemList.Size(); i++)
|
||||
if (itemList[i].filter->filterSetID==filterSetID)
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
}
|
||||
unsigned MessageFilter::GetFilterSetCount(void) const
|
||||
{
|
||||
return filterList.Size();
|
||||
}
|
||||
int MessageFilter::GetFilterSetIDByIndex(unsigned index)
|
||||
{
|
||||
return filterList[index]->filterSetID;
|
||||
}
|
||||
void MessageFilter::DeleteFilterSet(int filterSetID)
|
||||
{
|
||||
FilterSet *filterSet;
|
||||
bool objectExists;
|
||||
unsigned i,index;
|
||||
index = filterList.GetIndexFromKey(filterSetID, &objectExists);
|
||||
if (objectExists)
|
||||
{
|
||||
filterSet=filterList[index];
|
||||
DeallocateFilterSet(filterSet);
|
||||
filterList.RemoveAtIndex(index);
|
||||
|
||||
DataStructures::List< FilteredSystem > itemList;
|
||||
DataStructures::List< AddressOrGUID > keyList;
|
||||
systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_);
|
||||
for (i=0; i < itemList.Size(); i++)
|
||||
{
|
||||
if (itemList[i].filter==filterSet)
|
||||
{
|
||||
systemList.Remove(keyList[i], _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// Don't reference this pointer any longer
|
||||
i=0;
|
||||
while (i < systemList.Size())
|
||||
{
|
||||
if (systemList[i].filter==filterSet)
|
||||
systemList.RemoveAtIndex(i);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
void MessageFilter::Clear(void)
|
||||
{
|
||||
unsigned i;
|
||||
systemList.Clear(_FILE_AND_LINE_);
|
||||
for (i=0; i < filterList.Size(); i++)
|
||||
DeallocateFilterSet(filterList[i]);
|
||||
filterList.Clear(false, _FILE_AND_LINE_);
|
||||
}
|
||||
void MessageFilter::DeallocateFilterSet(FilterSet* filterSet)
|
||||
{
|
||||
RakNet::OP_DELETE(filterSet, _FILE_AND_LINE_);
|
||||
}
|
||||
FilterSet* MessageFilter::GetFilterSetByID(int filterSetID)
|
||||
{
|
||||
RakAssert(filterSetID>=0);
|
||||
bool objectExists;
|
||||
unsigned index;
|
||||
index = filterList.GetIndexFromKey(filterSetID, &objectExists);
|
||||
if (objectExists)
|
||||
return filterList[index];
|
||||
else
|
||||
{
|
||||
FilterSet *newFilterSet = RakNet::OP_NEW<FilterSet>( _FILE_AND_LINE_ );
|
||||
memset(newFilterSet->allowedIDs, 0, MESSAGE_FILTER_MAX_MESSAGE_ID * sizeof(bool));
|
||||
newFilterSet->banOnFilterTimeExceed=false;
|
||||
newFilterSet->kickOnDisallowedMessage=false;
|
||||
newFilterSet->banOnDisallowedMessage=false;
|
||||
newFilterSet->disallowedMessageBanTimeMS=0;
|
||||
newFilterSet->timeExceedBanTimeMS=0;
|
||||
newFilterSet->maxMemberTimeMS=0;
|
||||
newFilterSet->filterSetID=filterSetID;
|
||||
newFilterSet->invalidMessageCallback=0;
|
||||
newFilterSet->timeoutCallback=0;
|
||||
newFilterSet->timeoutUserData=0;
|
||||
filterList.Insert(filterSetID, newFilterSet, true, _FILE_AND_LINE_);
|
||||
return newFilterSet;
|
||||
}
|
||||
}
|
||||
void MessageFilter::OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID)
|
||||
{
|
||||
if (filterSet->invalidMessageCallback)
|
||||
filterSet->invalidMessageCallback(rakPeerInterface, systemAddress, filterSet->filterSetID, filterSet->disallowedCallbackUserData, messageID);
|
||||
if (filterSet->banOnDisallowedMessage && rakPeerInterface)
|
||||
{
|
||||
char str1[64];
|
||||
systemAddress.systemAddress.ToString(false, str1);
|
||||
rakPeerInterface->AddToBanList(str1, filterSet->disallowedMessageBanTimeMS);
|
||||
}
|
||||
if (filterSet->kickOnDisallowedMessage)
|
||||
{
|
||||
if (rakPeerInterface)
|
||||
rakPeerInterface->CloseConnection(systemAddress, true, 0);
|
||||
#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
else
|
||||
packetizedTCP->CloseConnection(systemAddress.systemAddress);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
void MessageFilter::Update(void)
|
||||
{
|
||||
// Update all timers for all systems. If those systems' filter sets are expired, take the appropriate action.
|
||||
RakNet::Time curTime = RakNet::GetTime();
|
||||
if (GreaterThan(curTime - 1000, whenLastTimeoutCheck))
|
||||
{
|
||||
DataStructures::List< FilteredSystem > itemList;
|
||||
DataStructures::List< AddressOrGUID > keyList;
|
||||
systemList.GetAsList(itemList, keyList, _FILE_AND_LINE_);
|
||||
|
||||
unsigned int index;
|
||||
for (index=0; index < itemList.Size(); index++)
|
||||
{
|
||||
if (itemList[index].filter &&
|
||||
itemList[index].filter->maxMemberTimeMS>0 &&
|
||||
curTime-itemList[index].timeEnteredThisSet >= itemList[index].filter->maxMemberTimeMS)
|
||||
{
|
||||
if (itemList[index].filter->timeoutCallback)
|
||||
itemList[index].filter->timeoutCallback(rakPeerInterface, keyList[index], itemList[index].filter->filterSetID, itemList[index].filter->timeoutUserData);
|
||||
|
||||
if (itemList[index].filter->banOnFilterTimeExceed && rakPeerInterface)
|
||||
{
|
||||
char str1[64];
|
||||
keyList[index].ToString(false, str1);
|
||||
rakPeerInterface->AddToBanList(str1, itemList[index].filter->timeExceedBanTimeMS);
|
||||
}
|
||||
if (rakPeerInterface)
|
||||
rakPeerInterface->CloseConnection(keyList[index], true, 0);
|
||||
#if _RAKNET_SUPPORT_PacketizedTCP==1 && _RAKNET_SUPPORT_TCPInterface==1
|
||||
else
|
||||
packetizedTCP->CloseConnection(keyList[index].systemAddress);
|
||||
#endif
|
||||
|
||||
systemList.Remove(keyList[index], _FILE_AND_LINE_);
|
||||
}
|
||||
}
|
||||
|
||||
whenLastTimeoutCheck=curTime+1000;
|
||||
}
|
||||
}
|
||||
void MessageFilter::OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming)
|
||||
{
|
||||
(void) systemAddress;
|
||||
(void) rakNetGUID;
|
||||
(void) isIncoming;
|
||||
|
||||
AddressOrGUID aog;
|
||||
aog.rakNetGuid=rakNetGUID;
|
||||
aog.systemAddress=systemAddress;
|
||||
|
||||
// New system, automatically assign to filter set if appropriate
|
||||
if (autoAddNewConnectionsToFilter>=0 && systemList.HasData(aog)==false)
|
||||
SetSystemFilterSet(aog, autoAddNewConnectionsToFilter);
|
||||
}
|
||||
void MessageFilter::OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason )
|
||||
{
|
||||
(void) rakNetGUID;
|
||||
(void) lostConnectionReason;
|
||||
|
||||
AddressOrGUID aog;
|
||||
aog.rakNetGuid=rakNetGUID;
|
||||
aog.systemAddress=systemAddress;
|
||||
|
||||
// Lost system, remove from the list
|
||||
systemList.Remove(aog, _FILE_AND_LINE_);
|
||||
}
|
||||
PluginReceiveResult MessageFilter::OnReceive(Packet *packet)
|
||||
{
|
||||
DataStructures::HashIndex index;
|
||||
unsigned char messageId;
|
||||
|
||||
switch (packet->data[0])
|
||||
{
|
||||
case ID_NEW_INCOMING_CONNECTION:
|
||||
case ID_CONNECTION_REQUEST_ACCEPTED:
|
||||
case ID_CONNECTION_LOST:
|
||||
case ID_DISCONNECTION_NOTIFICATION:
|
||||
case ID_CONNECTION_ATTEMPT_FAILED:
|
||||
case ID_NO_FREE_INCOMING_CONNECTIONS:
|
||||
case ID_IP_RECENTLY_CONNECTED:
|
||||
case ID_CONNECTION_BANNED:
|
||||
case ID_INVALID_PASSWORD:
|
||||
case ID_UNCONNECTED_PONG:
|
||||
case ID_ALREADY_CONNECTED:
|
||||
case ID_ADVERTISE_SYSTEM:
|
||||
case ID_REMOTE_DISCONNECTION_NOTIFICATION:
|
||||
case ID_REMOTE_CONNECTION_LOST:
|
||||
case ID_REMOTE_NEW_INCOMING_CONNECTION:
|
||||
case ID_DOWNLOAD_PROGRESS:
|
||||
break;
|
||||
default:
|
||||
if (packet->data[0]==ID_TIMESTAMP)
|
||||
{
|
||||
if (packet->length<sizeof(MessageID) + sizeof(RakNet::TimeMS))
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE; // Invalid message
|
||||
messageId=packet->data[sizeof(MessageID) + sizeof(RakNet::TimeMS)];
|
||||
}
|
||||
else
|
||||
messageId=packet->data[0];
|
||||
// If this system is filtered, check if this message is allowed. If not allowed, return RR_STOP_PROCESSING_AND_DEALLOCATE
|
||||
// index = systemList.GetIndexFromKey(packet->addressOrGUID, &objectExists);
|
||||
index = systemList.GetIndexOf(packet);
|
||||
if (index.IsInvalid())
|
||||
break;
|
||||
if (systemList.ItemAtIndex(index).filter->allowedIDs[messageId]==false)
|
||||
{
|
||||
OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
}
|
||||
if (packet->data[0]==ID_RPC_PLUGIN)
|
||||
{
|
||||
RakNet::BitStream bsIn(packet->data,packet->length,false);
|
||||
bsIn.IgnoreBytes(2);
|
||||
RakNet::RakString functionName;
|
||||
bsIn.ReadCompressed(functionName);
|
||||
if (systemList.ItemAtIndex(index).filter->allowedRPC4.HasData(functionName)==false)
|
||||
{
|
||||
OnInvalidMessage(systemList.ItemAtIndex(index).filter, packet, packet->data[0]);
|
||||
return RR_STOP_PROCESSING_AND_DEALLOCATE;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return RR_CONTINUE_PROCESSING;
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
191
third-party/RakNet/src/RakNet/MessageFilter.hpp
vendored
Normal file
191
third-party/RakNet/src/RakNet/MessageFilter.hpp
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
/// \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 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
#include "NativeFeatureIncludes.hpp"
|
||||
#if _RAKNET_SUPPORT_MessageFilter==1
|
||||
|
||||
#ifndef __MESSAGE_FILTER_PLUGIN_H
|
||||
#define __MESSAGE_FILTER_PLUGIN_H
|
||||
|
||||
#include "RakNetTypes.hpp"
|
||||
#include "PluginInterface2.hpp"
|
||||
#include "DS_OrderedList.hpp"
|
||||
#include "DS_Hash.hpp"
|
||||
#include "Export.hpp"
|
||||
|
||||
/// MessageIdentifier (ID_*) values shoudln't go higher than this. Change it if you do.
|
||||
#define MESSAGE_FILTER_MAX_MESSAGE_ID 256
|
||||
|
||||
namespace RakNet
|
||||
{
|
||||
/// Forward declarations
|
||||
class RakPeerInterface;
|
||||
|
||||
/// \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;
|
||||
RakNet::TimeMS disallowedMessageBanTimeMS;
|
||||
RakNet::TimeMS timeExceedBanTimeMS;
|
||||
RakNet::TimeMS maxMemberTimeMS;
|
||||
void (*invalidMessageCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData, unsigned char messageID);
|
||||
void *disallowedCallbackUserData;
|
||||
void (*timeoutCallback)(RakPeerInterface *peer, AddressOrGUID systemAddress, int filterSetID, void *userData);
|
||||
void *timeoutUserData;
|
||||
int filterSetID;
|
||||
bool allowedIDs[MESSAGE_FILTER_MAX_MESSAGE_ID];
|
||||
DataStructures::OrderedList<RakNet::RakString,RakNet::RakString> allowedRPC4;
|
||||
};
|
||||
|
||||
/// \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
|
||||
{
|
||||
FilterSet *filter;
|
||||
RakNet::TimeMS timeEnteredThisSet;
|
||||
};
|
||||
|
||||
/// \defgroup MESSAGEFILTER_GROUP MessageFilter
|
||||
/// \brief Remote incoming packets from unauthorized systems
|
||||
/// \details
|
||||
/// \ingroup PLUGINS_GROUP
|
||||
|
||||
/// \brief Assigns systems to FilterSets. Each FilterSet limits what kinds of messages are allowed.
|
||||
/// \details The MessageFilter plugin is used for security where you limit what systems can send what kind of messages.<BR>
|
||||
/// You implicitly define FilterSets, and add allowed message IDs to these FilterSets.<BR>
|
||||
/// You then add systems to these filters, such that those systems are limited to sending what the filters allows.<BR>
|
||||
/// You can automatically assign systems to a filter.<BR>
|
||||
/// You can automatically kick and possibly ban users that stay in a filter too long, or send the wrong message.<BR>
|
||||
/// Each system is a member of either zero or one filters.<BR>
|
||||
/// 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 PluginInterface2
|
||||
{
|
||||
public:
|
||||
|
||||
// GetInstance() and DestroyInstance(instance*)
|
||||
STATIC_FACTORY_DECLARATIONS(MessageFilter)
|
||||
|
||||
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 a specific RPC4 call
|
||||
/// \pre MessageFilter must be attached before RPC4
|
||||
/// \param[in] uniqueID Identifier passed to RegisterFunction()
|
||||
/// \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 SetAllowRPC4(bool allow, const char* uniqueID, 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, RakNet::TimeMS 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, AddressOrGUID addressOrGUID, 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, AddressOrGUID addressOrGUID, 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, RakNet::TimeMS banTimeMS, int filterSetID);
|
||||
|
||||
/// Get the filterSetID a system is using. Returns -1 for none.
|
||||
/// \param[in] addressOrGUID The system we are referring to
|
||||
int GetSystemFilterSet(AddressOrGUID addressOrGUID);
|
||||
|
||||
/// 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] addressOrGUID 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(AddressOrGUID addressOrGUID, 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 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 Update(void);
|
||||
virtual PluginReceiveResult OnReceive(Packet *packet);
|
||||
virtual void OnNewConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, bool isIncoming);
|
||||
virtual void OnClosedConnection(const SystemAddress &systemAddress, RakNetGUID rakNetGUID, PI2_LostConnectionReason lostConnectionReason );
|
||||
|
||||
protected:
|
||||
|
||||
void Clear(void);
|
||||
void DeallocateFilterSet(FilterSet *filterSet);
|
||||
FilterSet* GetFilterSetByID(int filterSetID);
|
||||
void OnInvalidMessage(FilterSet *filterSet, AddressOrGUID systemAddress, unsigned char messageID);
|
||||
|
||||
DataStructures::OrderedList<int, FilterSet*, FilterSetComp> filterList;
|
||||
// Change to guid
|
||||
DataStructures::Hash<AddressOrGUID, FilteredSystem, 2048, AddressOrGUID::ToInteger> systemList;
|
||||
|
||||
int autoAddNewConnectionsToFilter;
|
||||
RakNet::Time whenLastTimeoutCheck;
|
||||
};
|
||||
|
||||
} // namespace RakNet
|
||||
|
||||
#endif
|
||||
|
||||
#endif // _RAKNET_SUPPORT_*
|
||||
403
third-party/RakNet/src/RakNet/MessageIdentifiers.hpp
vendored
Normal file
403
third-party/RakNet/src/RakNet/MessageIdentifiers.hpp
vendored
Normal file
@@ -0,0 +1,403 @@
|
||||
/// \file
|
||||
/// \brief All the message identifiers used by RakNet. Message identifiers comprise the first byte of any message.
|
||||
///
|
||||
/// This file is part of RakNet Copyright 2003 Jenkins Software LLC
|
||||
///
|
||||
/// Usage of RakNet is subject to the appropriate license agreement.
|
||||
|
||||
|
||||
#ifndef __MESSAGE_IDENTIFIERS_H
|
||||
#define __MESSAGE_IDENTIFIERS_H
|
||||
|
||||
#if defined(RAKNET_USE_CUSTOM_PACKET_IDS)
|
||||
#include "CustomPacketIdentifiers.hpp"
|
||||
#else
|
||||
|
||||
enum OutOfBandIdentifiers
|
||||
{
|
||||
ID_NAT_ESTABLISH_UNIDIRECTIONAL,
|
||||
ID_NAT_ESTABLISH_BIDIRECTIONAL,
|
||||
ID_NAT_TYPE_DETECT,
|
||||
ID_ROUTER_2_REPLY_TO_SENDER_PORT,
|
||||
ID_ROUTER_2_REPLY_TO_SPECIFIED_PORT,
|
||||
ID_ROUTER_2_MINI_PUNCH_REPLY,
|
||||
ID_ROUTER_2_MINI_PUNCH_REPLY_BOUNCE,
|
||||
ID_XBOX_360_VOICE,
|
||||
ID_XBOX_360_GET_NETWORK_ROOM,
|
||||
ID_XBOX_360_RETURN_NETWORK_ROOM,
|
||||
};
|
||||
|
||||
/// You should not edit the file MessageIdentifiers.h as it is a part of RakNet static library
|
||||
/// To define your own message id, define an enum following the code example that follows.
|
||||
///
|
||||
/// \code
|
||||
/// enum {
|
||||
/// ID_MYPROJECT_MSG_1 = ID_USER_PACKET_ENUM,
|
||||
/// ID_MYPROJECT_MSG_2,
|
||||
/// ...
|
||||
/// };
|
||||
/// \endcode
|
||||
///
|
||||
/// \note All these enumerations should be casted to (unsigned char) before writing them to RakNet::BitStream
|
||||
enum DefaultMessageIDTypes
|
||||
{
|
||||
//
|
||||
// RESERVED TYPES - DO NOT CHANGE THESE
|
||||
// All types from RakPeer
|
||||
//
|
||||
/// These types are never returned to the user.
|
||||
/// Ping from a connected system. Update timestamps (internal use only)
|
||||
ID_CONNECTED_PING,
|
||||
/// Ping from an unconnected system. Reply but do not update timestamps. (internal use only)
|
||||
ID_UNCONNECTED_PING,
|
||||
/// Ping from an unconnected system. Only reply if we have open connections. Do not update timestamps. (internal use only)
|
||||
ID_UNCONNECTED_PING_OPEN_CONNECTIONS,
|
||||
/// Pong from a connected system. Update timestamps (internal use only)
|
||||
ID_CONNECTED_PONG,
|
||||
/// A reliable packet to detect lost connections (internal use only)
|
||||
ID_DETECT_LOST_CONNECTIONS,
|
||||
/// C2S: Initial query: Header(1), OfflineMesageID(16), Protocol number(1), Pad(toMTU), sent with no fragment set.
|
||||
/// If protocol fails on server, returns ID_INCOMPATIBLE_PROTOCOL_VERSION to client
|
||||
ID_OPEN_CONNECTION_REQUEST_1,
|
||||
/// S2C: Header(1), OfflineMesageID(16), server GUID(8), HasSecurity(1), Cookie(4, if HasSecurity)
|
||||
/// , public key (if do security is true), MTU(2). If public key fails on client, returns ID_PUBLIC_KEY_MISMATCH
|
||||
ID_OPEN_CONNECTION_REPLY_1,
|
||||
/// C2S: Header(1), OfflineMesageID(16), Cookie(4, if HasSecurity is true on the server), clientSupportsSecurity(1 bit),
|
||||
/// handshakeChallenge (if has security on both server and client), remoteBindingAddress(6), MTU(2), client GUID(8)
|
||||
/// Connection slot allocated if cookie is valid, server is not full, GUID and IP not already in use.
|
||||
ID_OPEN_CONNECTION_REQUEST_2,
|
||||
/// S2C: Header(1), OfflineMesageID(16), server GUID(8), mtu(2), doSecurity(1 bit), handshakeAnswer (if do security is true)
|
||||
ID_OPEN_CONNECTION_REPLY_2,
|
||||
/// C2S: Header(1), GUID(8), Timestamp, HasSecurity(1), Proof(32)
|
||||
ID_CONNECTION_REQUEST,
|
||||
/// RakPeer - Remote system requires secure connections, pass a public key to RakPeerInterface::Connect()
|
||||
ID_REMOTE_SYSTEM_REQUIRES_PUBLIC_KEY,
|
||||
/// RakPeer - We passed a public key to RakPeerInterface::Connect(), but the other system did not have security turned on
|
||||
ID_OUR_SYSTEM_REQUIRES_SECURITY,
|
||||
/// RakPeer - Wrong public key passed to RakPeerInterface::Connect()
|
||||
ID_PUBLIC_KEY_MISMATCH,
|
||||
/// 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,
|
||||
/// If RakPeerInterface::Send() is called where PacketReliability contains _WITH_ACK_RECEIPT, then on a later call to
|
||||
/// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long,
|
||||
/// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message.
|
||||
/// This number will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_ACKED means that
|
||||
/// the message arrived
|
||||
ID_SND_RECEIPT_ACKED,
|
||||
/// If RakPeerInterface::Send() is called where PacketReliability contains UNRELIABLE_WITH_ACK_RECEIPT, then on a later call to
|
||||
/// RakPeerInterface::Receive() you will get ID_SND_RECEIPT_ACKED or ID_SND_RECEIPT_LOSS. The message will be 5 bytes long,
|
||||
/// and bytes 1-4 inclusive will contain a number in native order containing a number that identifies this message. This number
|
||||
/// will be returned by RakPeerInterface::Send() or RakPeerInterface::SendList(). ID_SND_RECEIPT_LOSS means that an ack for the
|
||||
/// message did not arrive (it may or may not have been delivered, probably not). On disconnect or shutdown, you will not get
|
||||
/// ID_SND_RECEIPT_LOSS for unsent messages, you should consider those messages as all lost.
|
||||
ID_SND_RECEIPT_LOSS,
|
||||
|
||||
|
||||
//
|
||||
// USER TYPES - DO NOT CHANGE THESE
|
||||
//
|
||||
|
||||
/// RakPeer - In a client/server environment, our connection request to the server has been accepted.
|
||||
ID_CONNECTION_REQUEST_ACCEPTED,
|
||||
/// RakPeer - Sent to the player when a connection request cannot be completed due to inability to connect.
|
||||
ID_CONNECTION_ATTEMPT_FAILED,
|
||||
/// RakPeer - Sent a connect request to a system we are currently connected to.
|
||||
ID_ALREADY_CONNECTED,
|
||||
/// RakPeer - A remote system has successfully connected.
|
||||
ID_NEW_INCOMING_CONNECTION,
|
||||
/// RakPeer - The system we attempted to connect to is not accepting new connections.
|
||||
ID_NO_FREE_INCOMING_CONNECTIONS,
|
||||
/// RakPeer - The system specified in Packet::systemAddress has disconnected from us. For the client, this would mean the
|
||||
/// server has shutdown.
|
||||
ID_DISCONNECTION_NOTIFICATION,
|
||||
/// RakPeer - Reliable packets cannot be delivered to the system specified in Packet::systemAddress. The connection to that
|
||||
/// system has been closed.
|
||||
ID_CONNECTION_LOST,
|
||||
/// RakPeer - We are banned from the system we attempted to connect to.
|
||||
ID_CONNECTION_BANNED,
|
||||
/// RakPeer - The remote system is using a password and has refused our connection because we did not set the correct password.
|
||||
ID_INVALID_PASSWORD,
|
||||
// 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,
|
||||
// Means that this IP address connected recently, and can't connect again as a security measure. See
|
||||
/// RakPeer::SetLimitIPConnectionFrequency()
|
||||
ID_IP_RECENTLY_CONNECTED,
|
||||
/// RakPeer - The sizeof(RakNetTime) bytes following this byte represent a value which is automatically modified by the difference
|
||||
/// in system times between the sender and the recipient. Requires that you call SetOccasionalPing.
|
||||
ID_TIMESTAMP,
|
||||
/// RakPeer - Pong from an unconnected system. First byte is ID_UNCONNECTED_PONG, second sizeof(RakNet::TimeMS) bytes is the ping,
|
||||
/// following bytes is system specific enumeration data.
|
||||
/// Read using bitstreams
|
||||
ID_UNCONNECTED_PONG,
|
||||
/// RakPeer - Inform a remote system of our IP/Port. On the recipient, all data past ID_ADVERTISE_SYSTEM is whatever was passed to
|
||||
/// the data parameter
|
||||
ID_ADVERTISE_SYSTEM,
|
||||
// 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,
|
||||
|
||||
/// ConnectionGraph2 plugin - In a client/server environment, a client other than ourselves has disconnected gracefully.
|
||||
/// Packet::systemAddress is modified to reflect the systemAddress of this client.
|
||||
ID_REMOTE_DISCONNECTION_NOTIFICATION,
|
||||
/// ConnectionGraph2 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,
|
||||
/// ConnectionGraph2 plugin: Bytes 1-4 = count. for (count items) contains {SystemAddress, RakNetGUID, 2 byte ping}
|
||||
ID_REMOTE_NEW_INCOMING_CONNECTION,
|
||||
|
||||
/// FileListTransfer plugin - Setup data
|
||||
ID_FILE_LIST_TRANSFER_HEADER,
|
||||
/// FileListTransfer plugin - A file
|
||||
ID_FILE_LIST_TRANSFER_FILE,
|
||||
// Ack for reference push, to send more of the file
|
||||
ID_FILE_LIST_REFERENCE_PUSH_ACK,
|
||||
|
||||
/// DirectoryDeltaTransfer plugin - Request from a remote system for a download of a directory
|
||||
ID_DDT_DOWNLOAD_REQUEST,
|
||||
|
||||
/// RakNetTransport plugin - Transport provider message, used for remote console
|
||||
ID_TRANSPORT_STRING,
|
||||
|
||||
/// ReplicaManager plugin - Create an object
|
||||
ID_REPLICA_MANAGER_CONSTRUCTION,
|
||||
/// ReplicaManager plugin - Changed scope of an object
|
||||
ID_REPLICA_MANAGER_SCOPE_CHANGE,
|
||||
/// ReplicaManager plugin - Serialized data of an object
|
||||
ID_REPLICA_MANAGER_SERIALIZE,
|
||||
/// ReplicaManager plugin - New connection, about to send all world objects
|
||||
ID_REPLICA_MANAGER_DOWNLOAD_STARTED,
|
||||
/// ReplicaManager plugin - Finished downloading all serialized objects
|
||||
ID_REPLICA_MANAGER_DOWNLOAD_COMPLETE,
|
||||
|
||||
/// RakVoice plugin - Open a communication channel
|
||||
ID_RAKVOICE_OPEN_CHANNEL_REQUEST,
|
||||
/// RakVoice plugin - Communication channel accepted
|
||||
ID_RAKVOICE_OPEN_CHANNEL_REPLY,
|
||||
/// RakVoice plugin - Close a communication channel
|
||||
ID_RAKVOICE_CLOSE_CHANNEL,
|
||||
/// RakVoice plugin - Voice data
|
||||
ID_RAKVOICE_DATA,
|
||||
|
||||
/// Autopatcher plugin - Get a list of files that have changed since a certain date
|
||||
ID_AUTOPATCHER_GET_CHANGELIST_SINCE_DATE,
|
||||
/// Autopatcher plugin - A list of files to create
|
||||
ID_AUTOPATCHER_CREATION_LIST,
|
||||
/// Autopatcher plugin - A list of files to delete
|
||||
ID_AUTOPATCHER_DELETION_LIST,
|
||||
/// Autopatcher plugin - A list of files to get patches for
|
||||
ID_AUTOPATCHER_GET_PATCH,
|
||||
/// Autopatcher plugin - A list of patches for a list of files
|
||||
ID_AUTOPATCHER_PATCH_LIST,
|
||||
/// Autopatcher plugin - Returned to the user: An error from the database repository for the autopatcher.
|
||||
ID_AUTOPATCHER_REPOSITORY_FATAL_ERROR,
|
||||
/// Autopatcher plugin - Finished getting all files from the autopatcher
|
||||
ID_AUTOPATCHER_FINISHED_INTERNAL,
|
||||
ID_AUTOPATCHER_FINISHED,
|
||||
/// Autopatcher plugin - Returned to the user: You must restart the application to finish patching.
|
||||
ID_AUTOPATCHER_RESTART_APPLICATION,
|
||||
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_PUNCHTHROUGH_REQUEST,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_GROUP_PUNCHTHROUGH_REQUEST,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_GROUP_PUNCHTHROUGH_REPLY,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_CONNECT_AT_TIME,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_GET_MOST_RECENT_PORT,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_CLIENT_READY,
|
||||
/// NATPunchthrough plugin: internal
|
||||
ID_NAT_GROUP_PUNCHTHROUGH_FAILURE_NOTIFICATION,
|
||||
|
||||
/// NATPunchthrough plugin: Destination system is not connected to the server. Bytes starting at offset 1 contains the
|
||||
/// RakNetGUID destination field of NatPunchthroughClient::OpenNAT().
|
||||
ID_NAT_TARGET_NOT_CONNECTED,
|
||||
/// NATPunchthrough plugin: Destination system is not responding to ID_NAT_GET_MOST_RECENT_PORT. Possibly the plugin is not installed.
|
||||
/// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT().
|
||||
ID_NAT_TARGET_UNRESPONSIVE,
|
||||
/// NATPunchthrough plugin: The server lost the connection to the destination system while setting up punchthrough.
|
||||
/// Possibly the plugin is not installed. Bytes starting at offset 1 contains the RakNetGUID destination
|
||||
/// field of NatPunchthroughClient::OpenNAT().
|
||||
ID_NAT_CONNECTION_TO_TARGET_LOST,
|
||||
/// NATPunchthrough plugin: This punchthrough is already in progress. Possibly the plugin is not installed.
|
||||
/// Bytes starting at offset 1 contains the RakNetGUID destination field of NatPunchthroughClient::OpenNAT().
|
||||
ID_NAT_ALREADY_IN_PROGRESS,
|
||||
/// NATPunchthrough plugin: This message is generated on the local system, and does not come from the network.
|
||||
/// packet::guid contains the destination field of NatPunchthroughClient::OpenNAT(). Byte 1 contains 1 if you are the sender, 0 if not
|
||||
ID_NAT_PUNCHTHROUGH_FAILED,
|
||||
/// NATPunchthrough plugin: Punchthrough succeeded. See packet::systemAddress and packet::guid. Byte 1 contains 1 if you are the sender,
|
||||
/// 0 if not. You can now use RakPeer::Connect() or other calls to communicate with this system.
|
||||
ID_NAT_PUNCHTHROUGH_SUCCEEDED,
|
||||
/// NATPunchthrough plugin: OpenNATGroup failed.
|
||||
/// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT()
|
||||
/// Data format starts at byte 1:<BR>
|
||||
/// (char) passedSystemsCount,<BR>
|
||||
/// (RakNetGuid, SystemAddress) (for passedSystemsCount),<BR>
|
||||
/// (char) ignoredSystemsCount (caused by ID_NAT_TARGET_NOT_CONNECTED, ID_NAT_CONNECTION_TO_TARGET_LOST, ID_NAT_TARGET_UNRESPONSIVE),<BR>
|
||||
/// RakNetGuid (for ignoredSystemsCount),<BR>
|
||||
/// (char) failedSystemsCount,<BR>
|
||||
/// RakNetGuid (for failedSystemsCount)<BR>
|
||||
ID_NAT_GROUP_PUNCH_FAILED,
|
||||
/// NATPunchthrough plugin: OpenNATGroup succeeded.
|
||||
/// packet::guid contains the facilitator field of NatPunchthroughClient::OpenNAT()
|
||||
/// See ID_NAT_GROUP_PUNCH_FAILED for data format
|
||||
ID_NAT_GROUP_PUNCH_SUCCEEDED,
|
||||
|
||||
/// ReadyEvent plugin - Set the ready state for a particular system
|
||||
/// First 4 bytes after the message contains the id
|
||||
ID_READY_EVENT_SET,
|
||||
/// ReadyEvent plugin - Unset the ready state for a particular system
|
||||
/// First 4 bytes after the message contains the id
|
||||
ID_READY_EVENT_UNSET,
|
||||
/// All systems are in state ID_READY_EVENT_SET
|
||||
/// First 4 bytes after the message contains the id
|
||||
ID_READY_EVENT_ALL_SET,
|
||||
/// \internal, do not process in your game
|
||||
/// 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,
|
||||
|
||||
// RPC3, RPC4Plugin error
|
||||
ID_RPC_REMOTE_ERROR,
|
||||
/// Plugin based replacement for RPC system
|
||||
ID_RPC_PLUGIN,
|
||||
|
||||
/// 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,
|
||||
|
||||
/// Informs user of a new host GUID. Packet::Guid contains this new host RakNetGuid. The old host can be read out using BitStream->Read(RakNetGuid) starting on byte 1
|
||||
/// This is not returned until connected to a remote system
|
||||
/// If the oldHost is UNASSIGNED_RAKNET_GUID, then this is the first time the host has been determined
|
||||
ID_FCM2_NEW_HOST,
|
||||
/// \internal For FullyConnectedMesh2 plugin
|
||||
ID_FCM2_REQUEST_FCMGUID,
|
||||
/// \internal For FullyConnectedMesh2 plugin
|
||||
ID_FCM2_RESPOND_CONNECTION_COUNT,
|
||||
/// \internal For FullyConnectedMesh2 plugin
|
||||
ID_FCM2_INFORM_FCMGUID,
|
||||
/// \internal For FullyConnectedMesh2 plugin
|
||||
ID_FCM2_UPDATE_MIN_TOTAL_CONNECTION_COUNT,
|
||||
|
||||
/// UDP proxy messages. Second byte indicates type.
|
||||
ID_UDP_PROXY_GENERAL,
|
||||
|
||||
/// SQLite3Plugin - execute
|
||||
ID_SQLite3_EXEC,
|
||||
/// SQLite3Plugin - Remote database is unknown
|
||||
ID_SQLite3_UNKNOWN_DB,
|
||||
/// Events happening with SQLiteClientLoggerPlugin
|
||||
ID_SQLLITE_LOGGER,
|
||||
|
||||
/// Sent to NatTypeDetectionServer
|
||||
ID_NAT_TYPE_DETECTION_REQUEST,
|
||||
/// Sent to NatTypeDetectionClient. Byte 1 contains the type of NAT detected.
|
||||
ID_NAT_TYPE_DETECTION_RESULT,
|
||||
|
||||
/// Used by the router2 plugin
|
||||
ID_ROUTER_2_INTERNAL,
|
||||
/// No path is available or can be established to the remote system
|
||||
/// Packet::guid contains the endpoint guid that we were trying to reach
|
||||
ID_ROUTER_2_FORWARDING_NO_PATH,
|
||||
/// \brief You can now call connect, ping, or other operations to the destination system.
|
||||
///
|
||||
/// Connect as follows:
|
||||
///
|
||||
/// RakNet::BitStream bs(packet->data, packet->length, false);
|
||||
/// bs.IgnoreBytes(sizeof(MessageID));
|
||||
/// RakNetGUID endpointGuid;
|
||||
/// bs.Read(endpointGuid);
|
||||
/// unsigned short sourceToDestPort;
|
||||
/// bs.Read(sourceToDestPort);
|
||||
/// char ipAddressString[32];
|
||||
/// packet->systemAddress.ToString(false, ipAddressString);
|
||||
/// rakPeerInterface->Connect(ipAddressString, sourceToDestPort, 0,0);
|
||||
ID_ROUTER_2_FORWARDING_ESTABLISHED,
|
||||
/// The IP address for a forwarded connection has changed
|
||||
/// Read endpointGuid and port as per ID_ROUTER_2_FORWARDING_ESTABLISHED
|
||||
ID_ROUTER_2_REROUTED,
|
||||
|
||||
/// \internal Used by the team balancer plugin
|
||||
ID_TEAM_BALANCER_INTERNAL,
|
||||
/// Cannot switch to the desired team because it is full. However, if someone on that team leaves, you will
|
||||
/// get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member.
|
||||
ID_TEAM_BALANCER_REQUESTED_TEAM_CHANGE_PENDING,
|
||||
/// Cannot switch to the desired team because all teams are locked. However, if someone on that team leaves,
|
||||
/// you will get ID_TEAM_BALANCER_SET_TEAM later. Byte 1 contains the team you requested to join.
|
||||
ID_TEAM_BALANCER_TEAMS_LOCKED,
|
||||
/// Team balancer plugin informing you of your team. Byte 1 contains the team you requested to join. Following bytes contain NetworkID of which member.
|
||||
ID_TEAM_BALANCER_TEAM_ASSIGNED,
|
||||
|
||||
/// Gamebryo Lightspeed integration
|
||||
ID_LIGHTSPEED_INTEGRATION,
|
||||
|
||||
/// XBOX integration
|
||||
ID_XBOX_LOBBY,
|
||||
|
||||
/// The password we used to challenge the other system passed, meaning the other system has called TwoWayAuthentication::AddPassword() with the same password we passed to TwoWayAuthentication::Challenge()
|
||||
/// You can read the identifier used to challenge as follows:
|
||||
/// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(RakNet::MessageID)); RakNet::RakString password; bs.Read(password);
|
||||
ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_SUCCESS,
|
||||
ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_SUCCESS,
|
||||
/// A remote system sent us a challenge using TwoWayAuthentication::Challenge(), and the challenge failed.
|
||||
/// If the other system must pass the challenge to stay connected, you should call RakPeer::CloseConnection() to terminate the connection to the other system.
|
||||
ID_TWO_WAY_AUTHENTICATION_INCOMING_CHALLENGE_FAILURE,
|
||||
/// The other system did not add the password we used to TwoWayAuthentication::AddPassword()
|
||||
/// You can read the identifier used to challenge as follows:
|
||||
/// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password);
|
||||
ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_FAILURE,
|
||||
/// The other system did not respond within a timeout threshhold. Either the other system is not running the plugin or the other system was blocking on some operation for a long time.
|
||||
/// You can read the identifier used to challenge as follows:
|
||||
/// RakNet::BitStream bs(packet->data, packet->length, false); bs.IgnoreBytes(sizeof(MessageID)); RakNet::RakString password; bs.Read(password);
|
||||
ID_TWO_WAY_AUTHENTICATION_OUTGOING_CHALLENGE_TIMEOUT,
|
||||
/// \internal
|
||||
ID_TWO_WAY_AUTHENTICATION_NEGOTIATION,
|
||||
|
||||
/// CloudClient / CloudServer
|
||||
ID_CLOUD_POST_REQUEST,
|
||||
ID_CLOUD_RELEASE_REQUEST,
|
||||
ID_CLOUD_GET_REQUEST,
|
||||
ID_CLOUD_GET_RESPONSE,
|
||||
ID_CLOUD_UNSUBSCRIBE_REQUEST,
|
||||
ID_CLOUD_SERVER_TO_SERVER_COMMAND,
|
||||
ID_CLOUD_SUBSCRIPTION_NOTIFICATION,
|
||||
|
||||
// So I can add more without changing user enumerations
|
||||
ID_RESERVED_1,
|
||||
ID_RESERVED_2,
|
||||
ID_RESERVED_3,
|
||||
ID_RESERVED_4,
|
||||
ID_RESERVED_5,
|
||||
ID_RESERVED_6,
|
||||
ID_RESERVED_7,
|
||||
ID_RESERVED_8,
|
||||
ID_RESERVED_9,
|
||||
|
||||
// For the user to use. Start your first enumeration at this value.
|
||||
ID_USER_PACKET_ENUM,
|
||||
//-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
};
|
||||
|
||||
#endif // RAKNET_USE_CUSTOM_PACKET_IDS
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user