Engine: cleanup data block sender/receiver and add types

* Move member vars to correct place, to match it with the engine.
* Added out-of-band network message ID's for the R5 engine.

Also implemented 'ServerDataBlockSender::SendDataBlock()' and 'ClientDataBlockReceiver::AcknowledgeTransmission()'. NOTE that these are currently not tested, and also not in use! The code uses the version stored in the vftable which is what the engine itself provides. These have been implemented for reference only. If they need to be used, they need to be thoroughly tested first!
This commit is contained in:
Kawe Mazidjatari 2024-04-05 18:06:36 +02:00
parent 99af2877a6
commit 74da5c7c20
11 changed files with 466 additions and 249 deletions

45
r5dev/common/proto_oob.h Normal file
View File

@ -0,0 +1,45 @@
//============ Copyright Valve Corporation, All rights reserved. ==============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#if !defined( PROTO_OOB_H )
#define PROTO_OOB_H
#ifdef _WIN32
#pragma once
#endif
#include "proto_version.h"
#define PORT_RCON 37015 // default RCON port, TCP
#define PORT_SERVER 37015 // Default server port, UDP/TCP
#define PORT_CLIENT 37005 // Default client port, UDP/TCP
// Out of band message id bytes
// Prefixes: S = server, C = client, A = any
#define C2S_CONNECT 'A' // client requests to connect
#define S2C_DISCONNECT 'B' // server requests to disconnect
#define C2S_CHALLENGE 'H' // + challenge value
#define S2C_CHALLENGE 'I' // + challenge value
#define S2C_CONNACCEPT 'J'
#define S2C_CONNREJECT 'K' // special protocol for rejected connections
// Generic Ping Request
#define A2A_PING 'L' // respond with an A2A_ACK
#define A2A_ACK 'M' // general acknowledgment without info
#define S2C_UNKNOWN_UISCRIPT 'N' // TODO: figure out what this does, see [r5apex + 0x288880]
// Data Block Request
#define S2C_DATABLOCK_FRAGMENT 'O' // data block fragment
#define C2S_DATABLOCK_ACK 'P' // data block fragment acknowledgment
// All OOB packet start with this sequence
#define CONNECTIONLESS_HEADER 0xFFFFFFFF
#endif

View File

@ -0,0 +1,17 @@
//============ Copyright Valve Corporation, All rights reserved. ==============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#if !defined( PROTO_VERSION_H )
#define PROTO_VERSION_H
#ifdef _WIN32
#pragma once
#endif
// The current network protocol version. Changing this makes clients and servers incompatible
#define PROTOCOL_VERSION 529
#endif

View File

@ -136,6 +136,8 @@ add_sources( SOURCE_GROUP "Shared"
"shared/base_rcon.h"
"shared/shared_rcon.cpp"
"shared/shared_rcon.h"
"shared/datablock.cpp"
"shared/datablock.h"
)
if( NOT ${PROJECT_NAME} STREQUAL "engine_ds" )
@ -207,7 +209,6 @@ add_sources( SOURCE_GROUP "Public"
"${ENGINE_SOURCE_DIR}/public/dt_send.h"
"${ENGINE_SOURCE_DIR}/public/dt_recv.h"
"${ENGINE_SOURCE_DIR}/public/datamap.h"
"${ENGINE_SOURCE_DIR}/public/idatablock.h"
"${ENGINE_SOURCE_DIR}/public/idebugoverlay.h"
"${ENGINE_SOURCE_DIR}/public/iengine.h"
"${ENGINE_SOURCE_DIR}/public/iserver.h"

View File

@ -1,60 +1,62 @@
//===========================================================================//
//
// Purpose: client side datablock receiver
// Purpose: client side data block receiver
//
//===========================================================================//
#include "engine/client/clientstate.h"
#include "datablock_receiver.h"
#include "common/proto_oob.h"
#include "engine/common.h"
#include "engine/host_cmd.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ClientDataBlockReceiver::~ClientDataBlockReceiver()
{
ClientDataBlockReceiver__Destructor(this);
}
//-----------------------------------------------------------------------------
// Purpose: send an ack back to the server to let them know
// we received the datablock
// we received the data block
//-----------------------------------------------------------------------------
void ClientDataBlockReceiver::AcknowledgeTransmission()
{
ClientDataBlockReceiver__AcknowledgeTransmission(this);
}
const CClientState* const cl = m_pClientState;
//-----------------------------------------------------------------------------
// Purpose: initialize the data block receiver context
//-----------------------------------------------------------------------------
void ClientDataBlockReceiver::StartBlockReceiver(const int transferSize, const double startTime)
{
m_bStartedRecv = true;
m_nTransferSize = transferSize;
m_nTotalBlocks = transferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (transferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0);
m_nBlockAckTick = 0;
m_flStartTime = startTime;
if (!cl)
{
Assert(0, "ClientDataBlockReceiver::AcknowledgeTransmission() called without a valid client handle!");
return;
}
memset(m_BlockStatus, 0, sizeof(m_BlockStatus));
}
const CNetChan* const chan = cl->m_NetChannel;
//-----------------------------------------------------------------------------
// Purpose: reset the data block receiver context
//-----------------------------------------------------------------------------
void ClientDataBlockReceiver::ResetBlockReceiver(const short transferNr)
{
m_nTransferNr = transferNr;
if (!chan)
{
Assert(0, "ClientDataBlockReceiver::AcknowledgeTransmission() called without a net channel!");
return;
}
m_bStartedRecv = false;
m_bCompletedRecv = false;
char dataBuf[DATABLOCK_FRAGMENT_PACKET_SIZE];
bf_write buf(&dataBuf, sizeof(dataBuf));
m_TransferId = 0;
m_nTotalBlocks = 0;
m_nBlockAckTick = 0;
m_flStartTime = 0.0;
buf.WriteLong(CONNECTIONLESS_HEADER);
buf.WriteByte(C2S_DATABLOCK_ACK);
memset(m_BlockStatus, 0, sizeof(m_BlockStatus));
buf.WriteShort(m_TransferId);
buf.WriteShort(m_nTransferNr);
for (int i = m_nTotalBlocks; (i--) > 0;)
{
if (m_BlockStatus[i])
{
// ack the last blockNr we recv'd and processed
buf.WriteShort(i);
break;
}
}
// send the data block ack packet
v_NET_SendPacket(NULL,
chan->GetSocket(),
chan->GetRemoteAddress(),
buf.GetData(),
buf.GetNumBytesWritten(),
NULL, false, NULL, true);
}
//-----------------------------------------------------------------------------

View File

@ -1,37 +1,21 @@
//===========================================================================//
//
// Purpose: client side data block receiver
//
//===========================================================================//
#ifndef DATABLOCK_RECEIVER_H
#define DATABLOCK_RECEIVER_H
#include "idatablock.h"
#include "engine/shared/datablock.h"
class CClientState;
class ClientDataBlockReceiver : public NetDataBlockReceiver
{
friend class CClientState;
public:
virtual ~ClientDataBlockReceiver();
virtual void AcknowledgeTransmission() override;
void StartBlockReceiver(const int transferSize, const double startTime);
void ResetBlockReceiver(const short transferNr);
bool ProcessDataBlock(const double startTime, const short transferId, const int transferSize,
const short counter, const short currentBlockId, const void* const blockBuffer, const int blockBufferBytes);
protected:
CClientState* m_pClientState;
bool m_bStartedRecv;
bool m_bCompletedRecv;
bool byte12;
short m_TransferId;
short m_nTransferNr;
bool m_bInitialized;
int m_nTransferSize;
int m_nTotalBlocks;
int m_nBlockAckTick;
double m_flStartTime;
bool m_BlockStatus[MAX_DATABLOCK_FRAGMENTS];
char* m_pScratchBuffer;
};
struct ClientDataBlockHeader_s
@ -41,7 +25,6 @@ struct ClientDataBlockHeader_s
};
// virtual methods
inline void*(*ClientDataBlockReceiver__Destructor)(ClientDataBlockReceiver* thisptr);
inline void*(*ClientDataBlockReceiver__AcknowledgeTransmission)(ClientDataBlockReceiver* thisptr);
// non-virtual methods
@ -54,16 +37,11 @@ class VClientDataBlockReceiver : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("ClientDataBlockReceiver::~ClientDataBlockReceiver", ClientDataBlockReceiver__Destructor);
LogFunAdr("ClientDataBlockReceiver::AcknowledgeTransmission", ClientDataBlockReceiver__AcknowledgeTransmission);
LogFunAdr("ClientDataBlockReceiver::ProcessDataBlock", ClientDataBlockReceiver__ProcessDataBlock);
}
virtual void GetFun(void) const
{
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74"
" 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24"
" ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? C6 41 12 00").GetPtr(ClientDataBlockReceiver__Destructor);
g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 4C 8B 51 08").GetPtr(ClientDataBlockReceiver__AcknowledgeTransmission);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 83 EC 30 0F B7 44 24 ??")

View File

@ -22,10 +22,16 @@ constexpr const char* DEFAULT_NET_ENCRYPTION_KEY = "WDNWLmJYQ2ZlM0VoTid3Yg==";
inline void*(*v_NET_Init)(bool bDeveloper);
inline void(*v_NET_SetKey)(netkey_t* pKey, const char* szHash);
inline void(*v_NET_Config)(void);
inline int(*v_NET_GetPacket)(int iSocket, uint8_t* pScratch, bool bEncrypted);
inline int(*v_NET_SendPacket)(CNetChan* pChan, int iSocket, const netadr_t& toAdr, const uint8_t* pData, unsigned int nLen, void* unused0, bool bCompress, void* unused1, bool bEncrypt);
inline bool(*v_NET_ReceiveDatagram)(int iSocket, netpacket_s* pInpacket, bool bRaw);
inline int(*v_NET_SendDatagram)(SOCKET s, void* pPayload, int iLenght, netadr_t* pAdr, bool bEncrypted);
inline bool(*v_NET_BufferToBufferCompress)(uint8_t* const dest, size_t* const destLen, uint8_t* const source, const size_t sourceLen);
inline unsigned int(*v_NET_BufferToBufferDecompress_LZSS)(CLZSS* lzss, unsigned char* pInput, unsigned char* pOutput, unsigned int unBufSize);
inline void(*v_NET_PrintFunc)(const char* fmt, ...);
///////////////////////////////////////////////////////////////////////////////
@ -57,10 +63,16 @@ class VNet : public IDetour
LogFunAdr("NET_Init", v_NET_Init);
LogFunAdr("NET_Config", v_NET_Config);
LogFunAdr("NET_SetKey", v_NET_SetKey);
LogFunAdr("NET_GetPacket", v_NET_GetPacket);
LogFunAdr("NET_SendPacket", v_NET_SendPacket);
LogFunAdr("NET_ReceiveDatagram", v_NET_ReceiveDatagram);
LogFunAdr("NET_SendDatagram", v_NET_SendDatagram);
LogFunAdr("NET_BufferToBufferCompress", v_NET_BufferToBufferCompress);
LogFunAdr("NET_BufferToBufferDecompress_LZSS", v_NET_BufferToBufferDecompress_LZSS);
LogFunAdr("NET_PrintFunc", v_NET_PrintFunc);
LogVarAdr("g_NetAdr", g_pNetAdr);
LogVarAdr("g_NetKey", g_pNetKey);
@ -71,6 +83,11 @@ class VNet : public IDetour
g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC F0 01 ??").GetPtr(v_NET_Init);
g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 57 C0").GetPtr(v_NET_Config);
g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 48 83 EC 20 48 8B F9 41 B8").GetPtr(v_NET_SetKey);
g_GameDll.FindPatternSIMD("48 8B C4 44 88 40 18 48 89 50 10 41 55").GetPtr(v_NET_GetPacket);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 4C 63 F2").GetPtr(v_NET_SendPacket);
g_GameDll.FindPatternSIMD("48 89 74 24 18 48 89 7C 24 20 55 41 54 41 55 41 56 41 57 48 8D AC 24 50 EB").GetPtr(v_NET_ReceiveDatagram);
g_GameDll.FindPatternSIMD("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 41 56 41 57 48 81 EC ?? 05 ?? ??").GetPtr(v_NET_SendDatagram);

View File

@ -1,27 +1,60 @@
//===========================================================================//
//
// Purpose: server side datablock sender
// Purpose: server side data block sender
//
//===========================================================================//
#include "engine/client/client.h"
#include "common/proto_oob.h"
#include "datablock_sender.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ServerDataBlockSender::~ServerDataBlockSender()
{
ServerDataBlockSender__Destructor(this);
}
//-----------------------------------------------------------------------------
// Purpose: sends the datablock
// Purpose: sends the data block
//-----------------------------------------------------------------------------
void ServerDataBlockSender::SendDataBlock(const short transferId, const int transferSize,
const short transferNr, const short blockNr, const uint8_t* const blockData, const int blockSize)
{
ServerDataBlockSender__SendDataBlock(this, transferId, transferSize,
transferNr, blockNr, blockData, blockSize);
const CClient* const cl = m_pClient;
if (!cl)
{
Assert(0, "ServerDataBlockSender::SendDataBlock() called without a valid client handle!");
return;
}
const CNetChan* const chan = cl->m_NetChannel;
if (!chan)
{
Assert(0, "ServerDataBlockSender::SendDataBlock() called without a valid net channel!");
return;
}
char dataBuf[DATABLOCK_FRAGMENT_PACKET_SIZE];
bf_write buf(&dataBuf, sizeof(dataBuf));
// msg data (gets processed on client's out of band packet handler)
buf.WriteLong(CONNECTIONLESS_HEADER);
buf.WriteByte(S2C_DATABLOCK_FRAGMENT);
// transfer info
buf.WriteByte(transferId);
buf.WriteLong(transferSize);
buf.WriteByte(transferNr);
// block info
buf.WriteByte(blockNr);
buf.WriteLong(blockSize);
// block data
buf.WriteBytes(blockData, blockSize);
// send the data block packet
v_NET_SendPacket(NULL,
chan->GetSocket(),
chan->GetRemoteAddress(),
buf.GetData(),
buf.GetNumBytesWritten(),
NULL, false, NULL, true);
}
//-----------------------------------------------------------------------------
@ -60,72 +93,6 @@ const char* ServerDataBlockSender::GetReceiverName() const
return m_pClient->m_szServerName;
}
//-----------------------------------------------------------------------------
// Purpose: initialize the data block sender context
//-----------------------------------------------------------------------------
void ServerDataBlockSender::StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName)
{
m_bMultiplayer = isMultiplayer;
m_nBlockAckTick = 0;
m_nTransferSize = transferSize + sizeof(ServerDataBlockHeader_s);
// calculate the number of data blocks we have, which get sent individually
// to the receiver
m_nTotalBlocks = m_nTransferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (m_nTransferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0);
strncpy(m_szDebugName, debugName, sizeof(m_szDebugName));
m_szDebugName[sizeof(m_szDebugName) - 1] = '\0';
// null status memory
memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus));
memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes));
m_bInitialized = true;
m_bStartedTransfer = false;
const double currentTime = Plat_FloatTime();
m_TimeLastSend = currentTime;
m_TimeCurrentSend = currentTime;
m_TimeFirstSend = currentTime;
m_nTotalSizeRemaining = 4096;
m_nBlockSendsAttempted = 0;
}
//-----------------------------------------------------------------------------
// Purpose: reset the data block sender context
//-----------------------------------------------------------------------------
void ServerDataBlockSender::ResetBlockSender(void)
{
if (!m_bInitialized)
return;
m_bInitialized = false;
m_bStartedTransfer = false;
m_nTransferId = 0;
m_nTransferSize = 0;
m_nTotalBlocks = 0;
m_nBlockAckTick = 0;
m_TimeCurrentSend = 0.0;
m_TimeFirstSend = 0.0;
m_nTotalSizeRemaining = 0;
m_TimeLastSend = 0.0;
m_szDebugName[0] = '\0';
m_bDumbDataBlockInfo = false;
m_nCurrentBlock = -1;
m_nBlockSendsAttempted = 0;
memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus));
memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes));
m_nTransferNr++;
}
//-----------------------------------------------------------------------------
// Purpose: write the whole data in the data block scratch buffer
//-----------------------------------------------------------------------------
@ -172,8 +139,10 @@ void ServerDataBlockSender::WriteDataBlock(const uint8_t* const sourceData, cons
memcpy(m_pScratchBuffer + sizeof(ServerDataBlockHeader_s), sourceData, actualDataSize);
}
// create the context
StartBlockSender(actualDataSize, isMultiplayer, debugName);
// NOTE: we copy data in the scratch buffer with an offset of
// sizeof(ServerDataBlockHeader_s), the header gets send up as well so we
// have to take this into account !!!
StartBlockSender(actualDataSize + sizeof(ServerDataBlockHeader_s), isMultiplayer, debugName);
ReleaseSRWLockExclusive(&m_Lock);
}

View File

@ -1,83 +1,24 @@
//===========================================================================//
//
// Purpose: server side data block sender
//
//===========================================================================//
#ifndef DATABLOCK_SENDER_H
#define DATABLOCK_SENDER_H
#include "idatablock.h"
#include "engine/shared/datablock.h"
class CClient;
class ServerDataBlockSender : public NetDataBlockSender
{
friend class CClient;
public:
virtual ~ServerDataBlockSender() override;
virtual void SendDataBlock(const short transferId, const int transferSize, const short transferNr,
const short blockNr, const uint8_t* const blockData, const int blockSize) override;
virtual float GetResendRate() const override;
virtual const char* GetReceiverName() const override;
void StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName);
void ResetBlockSender();
void WriteDataBlock(const uint8_t* const sourceData, const int dataSize, const bool isMultiplayer, const char* const debugName);
protected:
char pad_0008[56];
RTL_SRWLOCK m_Lock;
char pad_0048[56];
// the server side client handle that is our 'receiving' end
CClient* m_pClient;
char m_bInitialized;
char m_bStartedTransfer;
char m_bMultiplayer;
char field_8B;
// the current transfer id, and the global transfer count for this
// particular client. the transfer nr keeps getting incremented on
// each new context setup
short m_nTransferId;
short m_nTransferNr;
// the total transfer size for the data, and the number of blocks this data
// has been carved up to
int m_nTransferSize;
int m_nTotalBlocks;
// last block that has been ack'd, and the current block that is pending to
// be sent to the receiver
int m_nBlockAckTick;
int m_nCurrentBlock;
// the total number of bytes remaining to be sent, and the number of times
// we attempted to send data blocks
int m_nTotalSizeRemaining;
int m_nBlockSendsAttempted;
// the resend rate for this connection, which depends of the stability/loss
// and other factors computed from the netchan
float m_flResendRate;
char pad_00AC[4]; // padding, in case we want to stuff our own vars in here
// times used to determine when a data block has been sent, and how long it
// took to get this out and acknowledged
double m_TimeCurrentSend;
double m_TimeFirstSend;
double m_TimeLastSend;
// the last time we attempted to send this block, this gets updated when
// a data block hasn't been acknowledged in time and is being resent
double m_flBlockSendTimes[MAX_DATABLOCK_FRAGMENTS];
// the debug name used when details get dumped to the console
char m_szDebugName[MAX_DATABLOCK_DEBUG_NAME];
// if a data block has been acknowledged by the receiver, we mark that
// particular block as acknowledged
bool m_bBlockAckStatus[MAX_DATABLOCK_FRAGMENTS];
uint8_t* m_pScratchBuffer;
bool m_bDumbDataBlockInfo;
};
struct ServerDataBlock
@ -93,7 +34,6 @@ struct ServerDataBlockHeader_s
bool isCompressed;
};
inline void*(*ServerDataBlockSender__Destructor)(ServerDataBlockSender* thisptr);
inline void* (*ServerDataBlockSender__SendDataBlock)(ServerDataBlockSender* thisptr,
const short transferId, const int transferSize, const short transferNr,
const short blockNr, const uint8_t* const blockData, const int blockSize);
@ -103,15 +43,10 @@ class VServerDataBlockSender : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("ServerDataBlockSender::~ServerDataBlockSender", ServerDataBlockSender__Destructor);
LogFunAdr("ServerDataBlockSender::SendDataBlock", ServerDataBlockSender__SendDataBlock);
}
virtual void GetFun(void) const
{
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74"
" 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24"
" ?? 48 89 74 24 ?? 57 48 83 EC 20 33 F6 66 C7 81 ?? ?? ?? ?? ?? ??").GetPtr(ServerDataBlockSender__Destructor);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 99 ?? ?? ?? ??").GetPtr(ServerDataBlockSender__SendDataBlock);
}
virtual void GetVar(void) const { }

View File

@ -0,0 +1,112 @@
//===========================================================================//
//
// Purpose: data block sender & receiver
//
//===========================================================================//
#include "datablock.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
NetDataBlockSender::~NetDataBlockSender()
{
ResetBlockSender();
delete[] m_pScratchBuffer;
m_pScratchBuffer = nullptr;
m_pClient = nullptr;
}
//-----------------------------------------------------------------------------
// Purpose: reset the data block sender context
//-----------------------------------------------------------------------------
void NetDataBlockSender::ResetBlockSender(void)
{
m_bInitialized = false;
m_bStartedTransfer = false;
m_nTransferId = 0;
m_nTransferSize = 0;
m_nTotalBlocks = 0;
m_nBlockAckTick = 0;
m_TimeCurrentSend = 0.0;
m_TimeFirstSend = 0.0;
m_nTotalSizeRemaining = 0;
m_TimeLastSend = 0.0;
m_szDebugName[0] = '\0';
m_bDumbDataBlockInfo = false;
m_nCurrentBlock = DATABLOCK_INVALID_BLOCK_NR;
m_nBlockSendsAttempted = 0;
memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus));
memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes));
}
//-----------------------------------------------------------------------------
// Purpose: initialize the data block sender context
//-----------------------------------------------------------------------------
void NetDataBlockSender::StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName)
{
m_bMultiplayer = isMultiplayer;
m_nBlockAckTick = 0;
m_nTransferSize = transferSize;
// calculate the number of data blocks we have, which get sent individually
// to the receiver
m_nTotalBlocks = m_nTransferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (m_nTransferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0);
strncpy(m_szDebugName, debugName, sizeof(m_szDebugName));
m_szDebugName[sizeof(m_szDebugName) - 1] = '\0';
// null status memory
memset(m_bBlockAckStatus, 0, sizeof(m_bBlockAckStatus));
memset(m_flBlockSendTimes, 0, sizeof(m_flBlockSendTimes));
m_bInitialized = true;
m_bStartedTransfer = false;
const double currentTime = Plat_FloatTime();
m_TimeLastSend = currentTime;
m_TimeCurrentSend = currentTime;
m_TimeFirstSend = currentTime;
m_nTotalSizeRemaining = 4096;
m_nBlockSendsAttempted = 0;
}
//-----------------------------------------------------------------------------
// Purpose: initialize the data block receiver context
//-----------------------------------------------------------------------------
void NetDataBlockReceiver::StartBlockReceiver(const int transferSize, const double startTime)
{
m_bStartedRecv = true;
m_nTransferSize = transferSize;
m_nTotalBlocks = transferSize / MAX_DATABLOCK_FRAGMENT_SIZE + (transferSize % MAX_DATABLOCK_FRAGMENT_SIZE != 0);
m_nBlockAckTick = 0;
m_flStartTime = startTime;
memset(m_BlockStatus, 0, sizeof(m_BlockStatus));
}
//-----------------------------------------------------------------------------
// Purpose: reset the data block receiver context
//-----------------------------------------------------------------------------
void NetDataBlockReceiver::ResetBlockReceiver(const short transferNr)
{
m_nTransferNr = transferNr;
m_bStartedRecv = false;
m_bCompletedRecv = false;
m_TransferId = 0;
m_nTotalBlocks = 0;
m_nBlockAckTick = 0;
m_flStartTime = 0.0;
memset(m_BlockStatus, 0, sizeof(m_BlockStatus));
}

View File

@ -0,0 +1,173 @@
//===========================================================================//
//
// Purpose: data block sender & receiver
//
//===========================================================================//
#ifndef IDATABLOCK_H
#define IDATABLOCK_H
// the maximum size of each data block fragment
#define MAX_DATABLOCK_FRAGMENT_SIZE 1024
// the maximum amount of fragments per data block
#define MAX_DATABLOCK_FRAGMENTS 768
#define DATABLOCK_DEBUG_NAME_LEN 64
#define DATABLOCK_INVALID_BLOCK_NR -1
// the maximum size of a data block fragment packet (encoded header + actual data)
#define DATABLOCK_FRAGMENT_PACKET_SIZE (MAX_DATABLOCK_FRAGMENT_SIZE + 176)
//-----------------------------------------------------------------------------
// Forward decelerations
//-----------------------------------------------------------------------------
class CClient;
class CClientState;
abstract_class NetDataBlockSender
{
public:
virtual ~NetDataBlockSender();
virtual void SendDataBlock(const short transferId, const int transferSize,
const short transferNr, const short blockNr, const uint8_t* const blockData, const int blockSize) = 0;
virtual float GetResendRate() const = 0;
virtual const char* GetReceiverName() const = 0;
void StartBlockSender(const int transferSize, const bool isMultiplayer, const char* const debugName);
void ResetBlockSender();
protected:
char pad_0008[56];
RTL_SRWLOCK m_Lock;
char pad_0048[56];
// the server side client handle that is our 'receiving' end
CClient* m_pClient;
char m_bInitialized;
char m_bStartedTransfer;
char m_bMultiplayer;
char field_8B;
// the current transfer id, and the global transfer count for this
// particular client. the transfer nr keeps getting incremented on
// each new context setup
short m_nTransferId;
short m_nTransferNr;
// the total transfer size for the data, and the number of blocks this data
// has been carved up to
int m_nTransferSize;
int m_nTotalBlocks;
// last block that has been ack'd, and the current block that is pending to
// be sent to the receiver
int m_nBlockAckTick;
int m_nCurrentBlock;
// the total number of bytes remaining to be sent, and the number of times
// we attempted to send data blocks
int m_nTotalSizeRemaining;
int m_nBlockSendsAttempted;
// the resend rate for this connection, which depends of the stability/loss
// and other factors computed from the netchan
float m_flResendRate;
char pad_00AC[4]; // padding, in case we want to stuff our own vars in here
// times used to determine when a data block has been sent, and how long it
// took to get this out and acknowledged
double m_TimeCurrentSend;
double m_TimeFirstSend;
double m_TimeLastSend;
// the last time we attempted to send this block, this gets updated when
// a data block hasn't been acknowledged in time and is being resent
double m_flBlockSendTimes[MAX_DATABLOCK_FRAGMENTS];
// the debug name used when details get dumped to the console
char m_szDebugName[DATABLOCK_DEBUG_NAME_LEN];
// if a data block has been acknowledged by the receiver, we mark that
// particular block as acknowledged
bool m_bBlockAckStatus[MAX_DATABLOCK_FRAGMENTS];
uint8_t* m_pScratchBuffer;
bool m_bDumbDataBlockInfo;
};
abstract_class NetDataBlockReceiver
{
public:
virtual ~NetDataBlockReceiver() {};
// Called when cvar 'net_debugDataBlockReceiver' is set;
// currently a nullsub in the engine.
virtual void DebugDataBlockReceiver() {};
virtual void AcknowledgeTransmission() = 0;
void StartBlockReceiver(const int transferSize, const double startTime);
void ResetBlockReceiver(const short transferNr);
protected:
// the client side 'client' handle
CClientState* m_pClientState;
// whether the transfer has been started and completed
bool m_bStartedRecv;
bool m_bCompletedRecv;
bool byte12;
// the current transfer id, and the global transfer count for this
// particular client. the transfer nr keeps getting incremented on
// each new context setup
short m_TransferId;
short m_nTransferNr;
bool m_bInitialized;
// the total transfer size, and the # amount of blocks the data has been
// carved into
int m_nTransferSize;
int m_nTotalBlocks;
int m_nBlockAckTick;
// the time the data block receiver was started for this transfer
double m_flStartTime;
// if we successfully processed a data block, we mark it as processed
bool m_BlockStatus[MAX_DATABLOCK_FRAGMENTS];
char* m_pScratchBuffer;
};
inline void* (*NetDataBlockSender__Destructor)(NetDataBlockSender* thisptr);
inline void* (*NetDataBlockReceiver__Destructor)(NetDataBlockReceiver* thisptr);
///////////////////////////////////////////////////////////////////////////////
class VNetDataBlock : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("NetDataBlockSender::~NetDataBlockSender", NetDataBlockSender__Destructor);
LogFunAdr("NetDataBlockReceiver::~NetDataBlockReceiver", NetDataBlockReceiver__Destructor);
}
virtual void GetFun(void) const
{
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74"
" 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24"
" ?? 48 89 74 24 ?? 57 48 83 EC 20 33 F6 66 C7 81 ?? ?? ?? ?? ?? ??").GetPtr(NetDataBlockSender__Destructor);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 57 48 83 EC 20 8B DA 48 8B F9 E8 ?? ?? ?? ?? F6 C3 01 74"
" 0D BA ?? ?? ?? ?? 48 8B CF E8 ?? ?? ?? ?? 48 8B C7 48 8B 5C 24 ?? 48 83 C4 20 5F C3 CC CC CC CC CC CC CC CC CC CC CC CC 48 89 5C 24"
" ?? 48 89 74 24 ?? 57 48 83 EC 20 48 8D 05 ?? ?? ?? ?? C6 41 12 00").GetPtr(NetDataBlockReceiver__Destructor);
}
virtual void GetVar(void) const { }
virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const { }
};
///////////////////////////////////////////////////////////////////////////////
#endif // IDATABLOCK_H

View File

@ -1,32 +0,0 @@
#ifndef IDATABLOCK_H
#define IDATABLOCK_H
// the maximum size of each data block fragment
#define MAX_DATABLOCK_FRAGMENT_SIZE 1024
// the maximum amount of fragments per data block
#define MAX_DATABLOCK_FRAGMENTS 768
#define MAX_DATABLOCK_DEBUG_NAME 64
abstract_class NetDataBlockSender
{
public:
virtual ~NetDataBlockSender() {};
virtual void SendDataBlock(const short transferId, const int transferSize,
const short transferNr, const short blockNr, const uint8_t* const blockData, const int blockSize) = 0;
virtual float GetResendRate() const = 0;
virtual const char* GetReceiverName() const = 0;
};
abstract_class NetDataBlockReceiver
{
public:
virtual ~NetDataBlockReceiver() {};
// Called when cvar 'net_debugDataBlockReceiver' is set;
// currently a nullsub in the engine.
virtual void DebugDataBlockReceiver() {};
virtual void AcknowledgeTransmission() = 0;
};
#endif // IDATABLOCK_H