r5sdk/r5dev/engine/client/client.cpp
Kawe Mazidjatari ef69611435 Replace memalloc calls throughout entire SDK
Global 'direct' usage of 'MemAllocSingleton()' has been jettisoned. Where possible, smart pointers were used instead. During the refactor, the following bugs were addressed and fixed:
- The virtual destructor of 'CCVarIteratorInternal' was NOT called on destruction.
- Class function 'KeyValues::MakeCopy' did NOT calculate the buffer size of the wide string correctly, the original calculation was 'len+1*sizeof(wchar_t)', but should've been '(len+1)*sizeof(wchar_t)'.

Some other code changes include:
- Tier0 include 'memstd.h' has been moved above all thirdparty includes, to make sure the memalloc functions get shadowed with ours in third party libraries as well.
- RPak file paths string literals are now defines.
- 'DestroyOverlay' has been refactored to match the assembly of the game.
2023-06-26 22:34:24 +02:00

440 lines
16 KiB
C++

//===============================================================================//
//
// Purpose:
//
// $NoKeywords: $
//
//===============================================================================//
// client.cpp: implementation of the CClient class.
//
///////////////////////////////////////////////////////////////////////////////////
#include "core/stdafx.h"
#include "tier1/cvar.h"
#include "engine/server/server.h"
#include "engine/client/client.h"
//---------------------------------------------------------------------------------
// Purpose: gets the client from buffer by index
//---------------------------------------------------------------------------------
CClient* CClient::GetClient(int nIndex) const
{
return reinterpret_cast<CClient*>(
(reinterpret_cast<uintptr_t>(g_pClient) + (nIndex * sizeof(CClient))));
}
//---------------------------------------------------------------------------------
// Purpose: gets the client's team number
//---------------------------------------------------------------------------------
int64_t CClient::GetTeamNum() const
{
return m_iTeamNum;
}
//---------------------------------------------------------------------------------
// Purpose: gets the handle of this client
//---------------------------------------------------------------------------------
edict_t CClient::GetHandle(void) const
{
return m_nHandle;
}
//---------------------------------------------------------------------------------
// Purpose: gets the userID of this client
//---------------------------------------------------------------------------------
int CClient::GetUserID(void) const
{
return m_nUserID;
}
//---------------------------------------------------------------------------------
// Purpose: gets the nucleusID of this client
//---------------------------------------------------------------------------------
uint64_t CClient::GetNucleusID(void) const
{
return m_nNucleusID;
}
//---------------------------------------------------------------------------------
// Purpose: gets the signon state of this client
//---------------------------------------------------------------------------------
SIGNONSTATE CClient::GetSignonState(void) const
{
return m_nSignonState;
}
//---------------------------------------------------------------------------------
// Purpose: gets the persistence state of this client
//---------------------------------------------------------------------------------
PERSISTENCE CClient::GetPersistenceState(void) const
{
return m_nPersistenceState;
}
//---------------------------------------------------------------------------------
// Purpose: gets the net channel of this client
//---------------------------------------------------------------------------------
CNetChan* CClient::GetNetChan(void) const
{
return m_NetChannel;
}
//---------------------------------------------------------------------------------
// Purpose: gets the pointer to the server object
//---------------------------------------------------------------------------------
CServer* CClient::GetServer(void) const
{
return m_pServer;
}
//---------------------------------------------------------------------------------
// Purpose: gets the command tick
//---------------------------------------------------------------------------------
int CClient::GetCommandTick(void) const
{
return m_nCommandTick;
}
//---------------------------------------------------------------------------------
// Purpose: gets the name of this client (managed by server)
//---------------------------------------------------------------------------------
const char* CClient::GetServerName(void) const
{
return m_szServerName;
}
//---------------------------------------------------------------------------------
// Purpose: gets the name of this client (obtained from connectionless packet)
//---------------------------------------------------------------------------------
const char* CClient::GetClientName(void) const
{
return m_szClientName;
}
//---------------------------------------------------------------------------------
// Purpose: sets the handle of this client
//---------------------------------------------------------------------------------
void CClient::SetHandle(edict_t nHandle)
{
m_nHandle = nHandle;
}
//---------------------------------------------------------------------------------
// Purpose: sets the userID of this client
//---------------------------------------------------------------------------------
void CClient::SetUserID(uint32_t nUserID)
{
m_nUserID = nUserID;
}
//---------------------------------------------------------------------------------
// Purpose: sets the nucleusID of this client
//---------------------------------------------------------------------------------
void CClient::SetNucleusID(uint64_t nNucleusID)
{
m_nNucleusID = nNucleusID;
}
//---------------------------------------------------------------------------------
// Purpose: sets the signon state of this client
//---------------------------------------------------------------------------------
void CClient::SetSignonState(SIGNONSTATE nSignonState)
{
m_nSignonState = nSignonState;
}
//---------------------------------------------------------------------------------
// Purpose: sets the persistence state of this client
//---------------------------------------------------------------------------------
void CClient::SetPersistenceState(PERSISTENCE nPersistenceState)
{
m_nPersistenceState = nPersistenceState;
}
//---------------------------------------------------------------------------------
// Purpose: sets the net channel of this client
// !TODO : Remove this and rebuild INetChannel
//---------------------------------------------------------------------------------
void CClient::SetNetChan(CNetChan* pNetChan)
{
m_NetChannel = pNetChan;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client is connected to server
// Output : true if connected, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsConnected(void) const
{
return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_CONNECTED;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client is spawned to server
// Output : true if connected, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsSpawned(void) const
{
return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_NEW;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client is active to server
// Output : true if connected, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsActive(void) const
{
return m_nSignonState == SIGNONSTATE::SIGNONSTATE_FULL;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client's persistence data is available
// Output : true if available, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsPersistenceAvailable(void) const
{
return m_nPersistenceState >= PERSISTENCE::PERSISTENCE_AVAILABLE;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client's persistence data is ready
// Output : true if ready, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsPersistenceReady(void) const
{
return m_nPersistenceState == PERSISTENCE::PERSISTENCE_READY;
}
//---------------------------------------------------------------------------------
// Purpose: checks if client is a fake client
// Output : true if connected, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsFakeClient(void) const
{
return m_bFakePlayer;
}
//---------------------------------------------------------------------------------
// Purpose: checks if this client is an actual human player
// Output : true if human, false otherwise
//---------------------------------------------------------------------------------
bool CClient::IsHumanPlayer(void) const
{
if (!IsConnected())
return false;
if (IsFakeClient())
return false;
return true;
}
//---------------------------------------------------------------------------------
// Purpose: throw away any residual garbage in the channel
//---------------------------------------------------------------------------------
void CClient::Clear(void)
{
#ifndef CLIENT_DLL
g_ServerPlayer[GetUserID()].Reset(); // Reset ServerPlayer slot.
#endif // !CLIENT_DLL
v_CClient_Clear(this);
}
//---------------------------------------------------------------------------------
// Purpose: throw away any residual garbage in the channel
// Input : *pClient -
//---------------------------------------------------------------------------------
void CClient::VClear(CClient* pClient)
{
#ifndef CLIENT_DLL
g_ServerPlayer[pClient->GetUserID()].Reset(); // Reset ServerPlayer slot.
#endif // !CLIENT_DLL
v_CClient_Clear(pClient);
}
//---------------------------------------------------------------------------------
// Purpose: connect new client
// Input : *szName -
// *pNetChannel -
// bFakePlayer -
// *a5 -
// *szMessage -
// nMessageSize -
// Output : true if connection was successful, false otherwise
//---------------------------------------------------------------------------------
bool CClient::Connect(const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize)
{
return v_CClient_Connect(this, szName, pNetChannel, bFakePlayer, a5, szMessage, nMessageSize);
}
//---------------------------------------------------------------------------------
// Purpose: connect new client
// Input : *pClient -
// *szName -
// *pNetChannel -
// bFakePlayer -
// *a5 -
// *szMessage -
// nMessageSize -
// Output : true if connection was successful, false otherwise
//---------------------------------------------------------------------------------
bool CClient::VConnect(CClient* pClient, const char* szName, void* pNetChannel, bool bFakePlayer, void* a5, char* szMessage, int nMessageSize)
{
bool bResult = v_CClient_Connect(pClient, szName, pNetChannel, bFakePlayer, a5, szMessage, nMessageSize);
#ifndef CLIENT_DLL
g_ServerPlayer[pClient->GetUserID()].Reset(); // Reset ServerPlayer slot.
#endif // !CLIENT_DLL
return bResult;
}
//---------------------------------------------------------------------------------
// Purpose: disconnect client
// Input : nRepLvl -
// *szReason -
// ... -
//---------------------------------------------------------------------------------
void CClient::Disconnect(const Reputation_t nRepLvl, const char* szReason, ...)
{
if (m_nSignonState != SIGNONSTATE::SIGNONSTATE_NONE)
{
char szBuf[1024];
{/////////////////////////////
va_list vArgs;
va_start(vArgs, szReason);
vsnprintf(szBuf, sizeof(szBuf), szReason, vArgs);
szBuf[sizeof(szBuf) - 1] = '\0';
va_end(vArgs);
}/////////////////////////////
v_CClient_Disconnect(this, nRepLvl, szBuf);
}
}
//---------------------------------------------------------------------------------
// Purpose: activate player
// Input : *pClient -
//---------------------------------------------------------------------------------
void CClient::VActivatePlayer(CClient* pClient)
{
pClient->SetPersistenceState(PERSISTENCE::PERSISTENCE_READY); // Set the client instance to 'ready'.
int nUserID = pClient->GetUserID();
if (!g_ServerPlayer[nUserID].m_bPersistenceEnabled && sv_showconnecting->GetBool())
{
g_ServerPlayer[nUserID].m_bPersistenceEnabled = true;
CNetChan* pNetChan = pClient->GetNetChan();
DevMsg(eDLL_T::SERVER, "Enabled persistence for client #%d; channel %s(%s) ('%llu')\n",
nUserID, pNetChan->GetName(), pNetChan->GetAddress(), pClient->GetNucleusID());
} ///////////////////////////////////////////////////////////////////////
v_CClient_ActivatePlayer(pClient);
}
//---------------------------------------------------------------------------------
// Purpose: send a net message with replay.
// set 'CNetMessage::m_nGroup' to 'NoReplay' to disable replay.
// Input : *pMsg -
// bLocal -
// bForceReliable -
// bVoice -
//---------------------------------------------------------------------------------
bool CClient::SendNetMsgEx(CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice)
{
if (!ShouldReplayMessage(pMsg))
{
// Don't copy the message into the replay buffer.
pMsg->m_nGroup = NetMessageGroup::NoReplay;
}
return v_CClient_SendNetMsgEx(this, pMsg, bLocal, bForceReliable, bVoice);
}
//---------------------------------------------------------------------------------
// Purpose: send a snapshot
// Input : *pClient -
// *pFrame -
// nTick -
// nTickAck -
//---------------------------------------------------------------------------------
void* CClient::VSendSnapshot(CClient* pClient, CClientFrame* pFrame, int nTick, int nTickAck)
{
return v_CClient_SendSnapshot(pClient, pFrame, nTick, nTickAck);
}
//---------------------------------------------------------------------------------
// Purpose: process string commands (kicking anyone attempting to DOS)
// Input : *pClient - (ADJ)
// *pMsg -
// Output : false if cmd should be passed to CServerGameClients
//---------------------------------------------------------------------------------
bool CClient::VProcessStringCmd(CClient* pClient, NET_StringCmd* pMsg)
{
#ifndef CLIENT_DLL
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
CClient* pClient_Adj = pClient;
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
/* Original function called method "CClient::ExecuteStringCommand" with an optimization
* that shifted the 'this' pointer with 8 bytes.
* Since this has been inlined with "CClient::ProcessStringCmd" as of S2, the shifting
* happens directly to anything calling this function. */
char* pShifted = reinterpret_cast<char*>(pClient) - 8;
CClient* pClient_Adj = reinterpret_cast<CClient*>(pShifted);
#endif // !GAMEDLL_S0 || !GAMEDLL_S1
int nUserID = pClient_Adj->GetUserID();
ServerPlayer_t* pSlot = &g_ServerPlayer[nUserID];
double flStartTime = Plat_FloatTime();
int nCmdQuotaLimit = sv_quota_stringCmdsPerSecond->GetInt();
if (!nCmdQuotaLimit)
return true;
const char* pCmd = pMsg->cmd;
// Just skip if the cmd pointer is null, we still check if the
// client sent too many commands and take appropriate actions.
// The internal function discards the command if it's null.
if (pCmd && !IsValidUTF8(pCmd))
{
Warning(eDLL_T::SERVER, "Removing client '%s' from slot #%i ('%llu' sent invalid string command!)\n",
pClient_Adj->GetNetChan()->GetAddress(), pClient_Adj->GetUserID(), pClient_Adj->GetNucleusID());
pClient_Adj->Disconnect(Reputation_t::REP_MARK_BAD, "#DISCONNECT_INVALID_STRINGCMD");
return true;
}
if (flStartTime - pSlot->m_flStringCommandQuotaTimeStart >= 1.0)
{
pSlot->m_flStringCommandQuotaTimeStart = flStartTime;
pSlot->m_nStringCommandQuotaCount = 0;
}
++pSlot->m_nStringCommandQuotaCount;
if (pSlot->m_nStringCommandQuotaCount > nCmdQuotaLimit)
{
Warning(eDLL_T::SERVER, "Removing client '%s' from slot #%i ('%llu' exceeded string command quota!)\n",
pClient_Adj->GetNetChan()->GetAddress(), pClient_Adj->GetUserID(), pClient_Adj->GetNucleusID());
pClient_Adj->Disconnect(Reputation_t::REP_MARK_BAD, "#DISCONNECT_STRINGCMD_OVERFLOW");
return true;
}
#endif // !CLIENT_DLL
return v_CClient_ProcessStringCmd(pClient, pMsg);
}
//---------------------------------------------------------------------------------
// Purpose: internal hook to 'CClient::SendNetMsgEx'
// Input : *pClient -
// *pMsg -
// bLocal -
// bForceReliable -
// bVoice -
//---------------------------------------------------------------------------------
bool CClient::VSendNetMsgEx(CClient* pClient, CNetMessage* pMsg, char bLocal, bool bForceReliable, bool bVoice)
{
return pClient->SendNetMsgEx(pMsg, bLocal, bForceReliable, bVoice);
}
///////////////////////////////////////////////////////////////////////////////
CClient* g_pClient = nullptr;