Kawe Mazidjatari 0d390955fd Engine: partial rebuild for data block sender/receiver
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.
2024-04-05 17:59:53 +02:00

309 lines
13 KiB
C++

#pragma once
#include "tier1/keyvalues.h"
#include "common/protocol.h"
#include "ebisusdk/EbisuTypes.h"
#include "engine/net.h"
#include "engine/net_chan.h"
#include "public/edict.h"
#include "engine/server/datablock_sender.h"
//-----------------------------------------------------------------------------
// Enumerations
//-----------------------------------------------------------------------------
enum Reputation_t
{
REP_NONE = 0,
REP_REMOVE_ONLY,
REP_MARK_BAD
};
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
class CServer;
class CClient;
class CClientExtended;
struct Spike_t
{
public:
Spike_t() :
m_nBits(0)
{
m_szDesc[0] = 0;
}
// !TODO: !unconfirmed!
char m_szDesc[64];
int m_nBits;
};
class CNetworkStatTrace
{
public:
CNetworkStatTrace() :
m_nStartBit(0), m_nCurBit(0), m_nMinWarningBytes(0)
{
}
int m_nStartBit;
int m_nCurBit;
int m_nMinWarningBytes;
CUtlVector< Spike_t > m_Records;
};
class CClientFrame
{
// !TODO: !unconfirmed!
int last_entity;
int tick_count;
CClientFrame* m_pNext;
};
///////////////////////////////////////////////////////////////////////////////
class CClient : IClientMessageHandler, INetChannelHandler
{
friend class ServerDataBlockSender;
public:
inline int64_t GetTeamNum() const { return m_iTeamNum; }
inline edict_t GetHandle(void) const { return m_nHandle; }
inline int GetUserID(void) const { return m_nUserID; }
inline NucleusID_t GetNucleusID(void) const { return m_nNucleusID; }
inline SIGNONSTATE GetSignonState(void) const { return m_nSignonState; }
inline PERSISTENCE GetPersistenceState(void) const { return m_nPersistenceState; }
inline CNetChan* GetNetChan(void) const { return m_NetChannel; }
inline CServer* GetServer(void) const { return m_pServer; }
#ifndef CLIENT_DLL
CClientExtended* GetClientExtended(void) const;
#endif // !CLIENT_DLL
inline int GetCommandTick(void) const { return m_nCommandTick; }
inline const char* GetServerName(void) const { return m_szServerName; }
inline const char* GetClientName(void) const { return m_szClientName; }
inline void SetHandle(edict_t nHandle) { m_nHandle = nHandle; }
inline void SetUserID(uint32_t nUserID) { m_nUserID = nUserID; }
inline void SetNucleusID(NucleusID_t nNucleusID) { m_nNucleusID = nNucleusID; }
inline void SetSignonState(SIGNONSTATE nSignonState) { m_nSignonState = nSignonState; }
inline void SetPersistenceState(PERSISTENCE nPersistenceState) { m_nPersistenceState = nPersistenceState; }
inline void SetNetChan(CNetChan* pNetChan) { m_NetChannel = pNetChan; }
inline bool IsConnected(void) const { return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_CONNECTED; }
inline bool IsSpawned(void) const { return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_NEW; }
inline bool IsActive(void) const { return m_nSignonState == SIGNONSTATE::SIGNONSTATE_FULL; }
inline bool IsPersistenceAvailable(void) const { return m_nPersistenceState >= PERSISTENCE::PERSISTENCE_AVAILABLE; }
inline bool IsPersistenceReady(void) const { return m_nPersistenceState == PERSISTENCE::PERSISTENCE_READY; }
inline bool IsFakeClient(void) const { return m_bFakePlayer; }
inline bool IsHumanPlayer(void) const { if (!IsConnected() || IsFakeClient()) { return false; } return true; }
bool SendNetMsgEx(CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice);
bool Authenticate(const char* const playerName, char* const reasonBuf, const size_t reasonBufLen);
bool Connect(const char* szName, CNetChan* pNetChan, bool bFakePlayer,
CUtlVector<NET_SetConVar::cvar_t>* conVars, char* szMessage, int nMessageSize);
void Disconnect(const Reputation_t nRepLvl, const char* szReason, ...);
void Clear(void);
public: // Hook statics:
static void VClear(CClient* pClient);
static bool VConnect(CClient* pClient, const char* szName, CNetChan* pNetChan, bool bFakePlayer,
CUtlVector<NET_SetConVar::cvar_t>* conVars, char* szMessage, int nMessageSize);
static void VActivatePlayer(CClient* pClient);
static void* VSendSnapshot(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck);
static bool VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice);
static void WriteDataBlock(CClient* pClient, bf_write& buf);
static bool VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg);
static bool VProcessSetConVar(CClient* pClient, NET_SetConVar* pMsg);
private:
// Stub reimplementation to avoid the 'no overrider' compiler errors in the
// CServer class (contains a static array of MAX_PLAYERS of this class).
virtual void* ConnectionStart(INetChannelHandler* chan) { return nullptr; }
virtual void ConnectionClosing(const char* reason, int unk) {}
virtual void ConnectionCrashed(const char* reason) {}
virtual void PacketStart(int incoming_sequence, int outgoing_acknowledged) {}
virtual void PacketEnd(void) {};
virtual void FileRequested(const char* fileName, unsigned int transferID) {}
virtual void ChannelDisconnect(const char* fileName) {}
virtual void* ProcessStringCmd(void) { return nullptr; }
virtual void* ProcessScriptMessage(void) { return nullptr; }
virtual void* ProcessSetConVar(void) { return nullptr; }
virtual bool nullsub_0(void) { return false; }
virtual char ProcessSignonState(void* msg) { return false; } // NET_SignonState
virtual void* ProcessMove(void) { return nullptr; }
virtual void* ProcessVoiceData(void) { return nullptr; }
virtual void* ProcessDurangoVoiceData(void) { return nullptr; }
virtual bool nullsub_1(void) { return false; }
virtual void* ProcessLoadingProgress(void) { return nullptr; }
virtual void* ProcessPersistenceRequestSave(void) { return nullptr; }
virtual bool nullsub_2(void) { return false; }
virtual bool nullsub_3(void) { return false; }
virtual void* ProcessSetPlaylistVarOverride(void) { return nullptr; }
virtual void* ProcessClaimClientSidePickup(void) { return nullptr; }
virtual void* ProcessCmdKeyValues(void) { return nullptr; }
virtual void* ProcessClientTick(void) { return nullptr; }
virtual void* ProcessClientSayText(void) { return nullptr; }
virtual bool nullsub_4(void) { return false; }
virtual bool nullsub_5(void) { return false; }
virtual bool nullsub_6(void) { return false; }
virtual void* ProcessScriptMessageChecksum(void) { return nullptr; }
private:
uint32_t m_nUserID;
edict_t m_nHandle;
char m_szServerName[256];
char m_szClientName[256];
char m_szMachineName[256];
int m_nCommandTick;
bool m_bUsePersistence_MAYBE;
char pad_0016[59];
int64_t m_iTeamNum;
KeyValues* m_ConVars;
bool m_bConVarsChanged;
bool m_bSendServerInfo;
bool m_bSendSignonData;
bool m_bFullStateAchieved;
char pad_0368[4];
CServer* m_pServer;
char pad_0378[20];
int m_nDisconnectTick;
bool m_bKickedByFairFight_MAYBE;
char pad_0398[3];
int m_nSendtableCRC;
int m_nMmDev;
char pad_039C[4];
CNetChan* m_NetChannel;
char pad_03A8[8];
SIGNONSTATE m_nSignonState;
int unk0;
NucleusID_t m_nNucleusID;
int unk1;
int unk2;
int m_nDeltaTick;
int m_nStringTableAckTick;
int m_nSignonTick;
int m_nBaselineUpdateTick_MAYBE;
char pad_03C0[448];
int unk3;
int m_nForceWaitForTick;
bool m_bFakePlayer;
bool m_bReceivedPacket;
bool m_bLowViolence;
bool m_bFullyAuthenticated;
char pad_05A4[24];
PERSISTENCE m_nPersistenceState;
char pad_05C0[48];
ServerDataBlock m_DataBlock;
char pad_4A3D8[60];
int m_LastMovementTick;
char pad_4A418[86];
char pad_4A46E[80];
};
static_assert(sizeof(CClient) == 0x4A4C0);
//-----------------------------------------------------------------------------
// Extended CClient class
//-----------------------------------------------------------------------------
// NOTE: since we interface directly with the engine, we cannot modify the
// client structure. In order to add new data to each client instance, we
// need to use this new class which we link directly to the corresponding
// client instance through its UserID.
//-----------------------------------------------------------------------------
class CClientExtended
{
friend class CClient;
public:
CClientExtended(void)
{
Reset();
}
inline void Reset(void)
{
m_flNetProcessingTimeMsecs = 0.0;
m_flNetProcessTimeBase = 0.0;
m_flStringCommandQuotaTimeStart = 0.0;
m_nStringCommandQuotaCount = NULL;
m_bInitialConVarsSet = false;
}
public: // Inlines:
inline void SetNetProcessingTimeMsecs(const double flStartTime, const double flCurrentTime)
{ m_flNetProcessingTimeMsecs = (flCurrentTime * 1000) - (flStartTime * 1000); }
inline double GetNetProcessingTimeMsecs(void) const { return m_flNetProcessingTimeMsecs; }
inline void SetNetProcessingTimeBase(const double flTime) { m_flNetProcessTimeBase = flTime; }
inline double GetNetProcessingTimeBase(void) const { return m_flNetProcessTimeBase; }
inline void SetStringCommandQuotaTimeStart(const double flTime) { m_flStringCommandQuotaTimeStart = flTime; }
inline double GetStringCommandQuotaTimeStart(void) const { return m_flStringCommandQuotaTimeStart; }
inline void SetStringCommandQuotaCount(const int iCount) { m_nStringCommandQuotaCount = iCount; }
inline int GetStringCommandQuotaCount(void) const { return m_nStringCommandQuotaCount; }
private:
// Measure how long this client's packets took to process.
double m_flNetProcessingTimeMsecs;
double m_flNetProcessTimeBase;
// The start time of the first stringcmd since reset.
double m_flStringCommandQuotaTimeStart;
int m_nStringCommandQuotaCount;
bool m_bInitialConVarsSet; // Whether or not the initial ConVar KV's are set
};
/* ==== CBASECLIENT ===================================================================================================================================================== */
inline bool(*CClient__Connect)(CClient* pClient, const char* szName, CNetChan* pNetChan, bool bFakePlayer, CUtlVector<NET_SetConVar::cvar_t>* conVars, char* szMessage, int nMessageSize);
inline bool(*CClient__Disconnect)(CClient* pClient, const Reputation_t nRepLvl, const char* szReason, ...);
inline void(*CClient__Clear)(CClient* pClient);
inline void(*CClient__ActivatePlayer)(CClient* pClient);
inline bool(*CClient__SetSignonState)(CClient* pClient, SIGNONSTATE signon);
inline bool(*CClient__SendNetMsgEx)(CClient* pClient, CNetMessage* pMsg, bool bLocal, bool bForceReliable, bool bVoice);
inline void*(*CClient__SendSnapshot)(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck);
inline void(*CClient__WriteDataBlock)(CClient* pClient, bf_write& buf);
inline bool(*CClient__ProcessStringCmd)(CClient* pClient, NET_StringCmd* pMsg);
inline bool(*CClient__ProcessSetConVar)(CClient* pClient, NET_SetConVar* pMsg);
///////////////////////////////////////////////////////////////////////////////
class VClient : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("CClient::Connect", CClient__Connect);
LogFunAdr("CClient::Disconnect", CClient__Disconnect);
LogFunAdr("CClient::Clear", CClient__Clear);
LogFunAdr("CClient::ActivatePlayer", CClient__ActivatePlayer);
LogFunAdr("CClient::SetSignonState", CClient__SetSignonState);
LogFunAdr("CClient::SendNetMsgEx", CClient__SendNetMsgEx);
LogFunAdr("CClient::SendSnapshot", CClient__SendSnapshot);
LogFunAdr("CClient::WriteDataBlock", CClient__WriteDataBlock);
LogFunAdr("CClient::ProcessStringCmd", CClient__ProcessStringCmd);
LogFunAdr("CClient::ProcessSetConVar", CClient__ProcessSetConVar);
}
virtual void GetFun(void) const
{
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 56 57 41 56 48 83 EC 20 41 0F B6 E9").GetPtr(CClient__Connect);
g_GameDll.FindPatternSIMD("48 8B C4 4C 89 40 18 4C 89 48 20 53 56 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 49 8B F8 8B F2").GetPtr(CClient__Disconnect);
g_GameDll.FindPatternSIMD("40 53 41 56 41 57 48 83 EC 20 48 8B D9 48 89 74").GetPtr(CClient__Clear);
g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 8B 81 B0 03 ?? ?? 48 8B D9 C6").GetPtr(CClient__ActivatePlayer);
g_GameDll.FindPatternSIMD("40 53 55 56 57 41 56 48 83 EC 40 48 8B 05 ?? ?? ?? ??").GetPtr(CClient__SendNetMsgEx);
g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 56 41 55 41 56 41 57 48 8D 6C 24 ??").GetPtr(CClient__SendSnapshot);
g_GameDll.FindPatternSIMD("40 53 57 48 83 EC 38 48 8B 05 ?? ?? ?? ??").GetPtr(CClient__WriteDataBlock);
g_GameDll.FindPatternSIMD("48 89 6C 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 7A 20").GetPtr(CClient__ProcessStringCmd);
g_GameDll.FindPatternSIMD("48 83 EC 28 48 83 C2 20").GetPtr(CClient__ProcessSetConVar);
g_GameDll.FindPatternSIMD("48 8B C4 48 89 58 10 48 89 70 18 57 48 81 EC ?? ?? ?? ?? 0F 29 70 E8 8B F2").GetPtr(CClient__SetSignonState);
}
virtual void GetVar(void) const { }
virtual void GetCon(void) const { }
virtual void Detour(const bool bAttach) const;
};
///////////////////////////////////////////////////////////////////////////////