mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
This patch partially rebuilds the data block sender/receiver. The receiver leaks memory if the sender sends a bogus LZ4 packet, it would allocate memory to copy the encoded data into, from which it would decode it to the scratch buffer, but it would never deallocate this temporary buffer is the LZ4 decoder failed. This has been fixed. The second reason to rebuild these was to look into potential compression optimization. The data block rebuild now also features the latest LZ4 codec.
180 lines
5.6 KiB
C++
180 lines
5.6 KiB
C++
//===========================================================================//
|
|
//
|
|
// Purpose: server side datablock sender
|
|
//
|
|
//===========================================================================//
|
|
#include "engine/client/client.h"
|
|
#include "datablock_sender.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
ServerDataBlockSender::~ServerDataBlockSender()
|
|
{
|
|
ServerDataBlockSender__Destructor(this);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sends the datablock
|
|
//-----------------------------------------------------------------------------
|
|
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);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: gets the resend rate
|
|
//-----------------------------------------------------------------------------
|
|
float ServerDataBlockSender::GetResendRate() const
|
|
{
|
|
const CClient* const pClient = m_pClient;
|
|
|
|
if (!pClient)
|
|
return 0.0f;
|
|
|
|
const CNetChan* const pChan = pClient->GetNetChan();
|
|
|
|
if (!pChan)
|
|
return 0.0f;
|
|
|
|
if (m_bStartedTransfer)
|
|
return 0.0f;
|
|
|
|
const float netResendRate = pChan->GetResendRate();
|
|
|
|
if (netResendRate < net_datablock_networkLossForSlowSpeed->GetFloat())
|
|
{
|
|
return m_flResendRate;
|
|
}
|
|
|
|
return netResendRate;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: gets the receiver name (client name as registered on the server)
|
|
//-----------------------------------------------------------------------------
|
|
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
|
|
//-----------------------------------------------------------------------------
|
|
void ServerDataBlockSender::WriteDataBlock(const uint8_t* const sourceData, const int dataSize,
|
|
const bool isMultiplayer, const char* const debugName)
|
|
{
|
|
AcquireSRWLockExclusive(&m_Lock);
|
|
|
|
ServerDataBlockHeader_s* const pHeader = reinterpret_cast<ServerDataBlockHeader_s*>(m_pScratchBuffer);
|
|
bool copyRaw = true;
|
|
|
|
int actualDataSize = dataSize;
|
|
|
|
if (net_compressDataBlock->GetBool())
|
|
{
|
|
const int encodedSize = LZ4_compress_fast((const char*)sourceData, (char*)m_pScratchBuffer + sizeof(ServerDataBlockHeader_s),
|
|
dataSize, SNAPSHOT_SCRATCH_BUFFER_SIZE, net_compressDataBlockLzAcceleration->GetInt());
|
|
|
|
// this shouldn't happen at all
|
|
if (!encodedSize)
|
|
{
|
|
Assert(0);
|
|
Error(eDLL_T::SERVER, 0, "LZ4 error compressing data block for client.\n");
|
|
}
|
|
|
|
// make sure the encoded data is smaller than the raw data, in some cases
|
|
// this might turn larger which means we should just send raw data
|
|
else if (encodedSize < dataSize)
|
|
{
|
|
actualDataSize = encodedSize;
|
|
|
|
pHeader->isCompressed = true;
|
|
copyRaw = false;
|
|
}
|
|
}
|
|
|
|
// in case no compression was performed, we send the raw data
|
|
if (copyRaw)
|
|
{
|
|
// this should equal the dataSize at this point, even if compression failed
|
|
Assert(actualDataSize == dataSize);
|
|
|
|
pHeader->isCompressed = false;
|
|
memcpy(m_pScratchBuffer + sizeof(ServerDataBlockHeader_s), sourceData, actualDataSize);
|
|
}
|
|
|
|
// create the context
|
|
StartBlockSender(actualDataSize, isMultiplayer, debugName);
|
|
|
|
ReleaseSRWLockExclusive(&m_Lock);
|
|
}
|