Check if we have enough room for new execution markers

Make sure we always have enough room for new execution markers. Engine normally truncates the head of the vector if out of room; we want to avoid it as this will cause the cookies to shift, and thus cause them to misalign with their respective commands.
This commit is contained in:
Kawe Mazidjatari 2023-11-25 17:41:08 +01:00
parent 699a4ce464
commit c86f0c5c6a
6 changed files with 218 additions and 23 deletions

View File

@ -13,11 +13,15 @@
#include "tier0/frametask.h"
#include "engine/common.h"
#include "engine/host.h"
#ifndef CLIENT_DLL
#include "engine/server/server.h"
#endif // !CLIENT_DLL
#include "clientstate.h"
#include "common/callback.h"
#include "cdll_engine_int.h"
#include "vgui/vgui_baseui_interface.h"
#include <ebisusdk/EbisuSDK.h>
#include <engine/cmd.h>
//------------------------------------------------------------------------------
@ -146,25 +150,58 @@ void CClientState::VConnectionClosing(CClientState* thisptr, const char* szReaso
// no longer can process server ticks every frame unlike previous games.
// Without this, the server CPU and frame time don't get updated to the client.
//------------------------------------------------------------------------------
bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick* pServerTick)
bool CClientState::VProcessServerTick(CClientState* thisptr, SVC_ServerTick* msg)
{
if (pServerTick->m_NetTick.m_nCommandTick != -1)
if (msg->m_NetTick.m_nCommandTick != -1)
{
return CClientState__ProcessServerTick(pClientState, pServerTick);
return CClientState__ProcessServerTick(thisptr, msg);
}
else // Statistics only.
{
char* pShifted = reinterpret_cast<char*>(pClientState) - 0x10; // Shifted due to compiler optimizations.
CClientState* pClient_Adj = reinterpret_cast<CClientState*>(pShifted);
CClientState* const thisptr_ADJ = thisptr->GetShiftedBasePointer();
CNetChan* pChan = pClient_Adj->m_NetChannel;
pChan->SetRemoteFramerate(pServerTick->m_NetTick.m_flHostFrameTime, pServerTick->m_NetTick.m_flHostFrameTimeStdDeviation);
pChan->SetRemoteCPUStatistics(pServerTick->m_NetTick.m_nServerCPU);
CNetChan* const pChan = thisptr_ADJ->m_NetChannel;
pChan->SetRemoteFramerate(msg->m_NetTick.m_flHostFrameTime, msg->m_NetTick.m_flHostFrameTimeStdDeviation);
pChan->SetRemoteCPUStatistics(msg->m_NetTick.m_nServerCPU);
return true;
}
}
bool CClientState::_ProcessStringCmd(CClientState* thisptr, NET_StringCmd* msg)
{
CClientState* const thisptr_ADJ = thisptr->GetShiftedBasePointer();
if (thisptr_ADJ->m_bRestrictServerCommands
#ifndef CLIENT_DLLInternalProcessStringCmd
&& !g_pServer->IsActive()
#endif // !CLIENT_DLL
)
{
CCommand args;
args.Tokenize(msg->cmd, cmd_source_t::kCommandSrcInvalid);
if (args.ArgC() > 0)
{
if (!Cbuf_AddTextWithMarkers(msg->cmd,
eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE,
eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE))
{
DevWarning(eDLL_T::CLIENT, "%s: No room for %i execution markers; command \"%s\" ignored\n",
__FUNCTION__, 2, msg->cmd);
}
return true;
}
}
else
{
Cbuf_AddText(Cbuf_GetCurrentPlayer(), msg->cmd, cmd_source_t::kCommandSrcCode);
}
return true;
}
//------------------------------------------------------------------------------
// Purpose: get authentication token for current connection context
// Input : *connectParams -
@ -251,6 +288,7 @@ void CClientState::VConnect(CClientState* thisptr, connectparams_t* connectParam
void VClientState::Attach() const
{
DetourAttach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing);
DetourAttach(&CClientState__ProcessStringCmd, &CClientState::_ProcessStringCmd);
DetourAttach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick);
DetourAttach(&CClientState__Connect, &CClientState::VConnect);
}
@ -258,6 +296,7 @@ void VClientState::Attach() const
void VClientState::Detach() const
{
DetourDetach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing);
DetourDetach(&CClientState__ProcessStringCmd, &CClientState::_ProcessStringCmd);
DetourDetach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick);
DetourDetach(&CClientState__Connect, &CClientState::VConnect);
}

View File

@ -36,6 +36,7 @@ class CClientState : CS_INetChannelHandler, IConnectionlessPacketHandler, IServe
friend class ClientDataBlockReceiver;
public: // Hook statics.
static void VConnectionClosing(CClientState* thisptr, const char* szReason);
static bool _ProcessStringCmd(CClientState* thisptr, NET_StringCmd* msg);
static bool VProcessServerTick(CClientState* thisptr, SVC_ServerTick* msg);
static void VConnect(CClientState* thisptr, connectparams_t* connectParams);
@ -59,6 +60,14 @@ public:
bool Authenticate(connectparams_t* connectParams, char* const reasonBuf, const size_t reasonBufLen) const;
protected:
FORCEINLINE CClientState* GetShiftedBasePointer(void)
{
char* const pShifted = reinterpret_cast<char*>(this) - 0x10; // Shifted due to compiler optimizations.
return reinterpret_cast<CClientState*>(pShifted);
}
public:
int m_Socket;
int _padding_maybe;
CNetChan* m_NetChannel;
@ -221,6 +230,9 @@ inline void(*CClientState__Disconnect)(CClientState* thisptr, bool bSendTracking
inline CMemory p_CClientState__ConnectionClosing;
inline void(*CClientState__ConnectionClosing)(CClientState* thisptr, const char* szReason);
inline CMemory p_CClientState__ProcessStringCmd;
inline bool(*CClientState__ProcessStringCmd)(CClientState* thisptr, NET_StringCmd* msg);
inline CMemory p_CClientState__ProcessServerTick;
inline bool(*CClientState__ProcessServerTick)(CClientState* thisptr, SVC_ServerTick* msg);
@ -233,6 +245,7 @@ class VClientState : public IDetour
LogFunAdr("CClientState::Connect", p_CClientState__Connect.GetPtr());
LogFunAdr("CClientState::Disconnect", p_CClientState__Disconnect.GetPtr());
LogFunAdr("CClientState::ConnectionClosing", p_CClientState__ConnectionClosing.GetPtr());
LogFunAdr("CClientState::ProcessStringCmd", p_CClientState__ProcessStringCmd.GetPtr());
LogFunAdr("CClientState::ProcessServerTick", p_CClientState__ProcessServerTick.GetPtr());
LogVarAdr("g_ClientState", reinterpret_cast<uintptr_t>(g_pClientState));
LogVarAdr("g_ClientState_Shifted", reinterpret_cast<uintptr_t>(g_pClientState_Shifted));
@ -249,6 +262,7 @@ class VClientState : public IDetour
p_CClientState__Disconnect = g_GameDll.FindPatternSIMD("40 56 57 41 54 41 55 41 57 48 83 EC 30 44 0F B6 FA");
p_CClientState__ConnectionClosing = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 0F 8E ?? ?? ?? ??");
#endif
p_CClientState__ProcessStringCmd = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 80 B9 ?? ?? ?? ?? ?? 48 8B DA");
p_CClientState__ProcessServerTick = g_GameDll.FindPatternSIMD("40 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B F9 7C 66");
p_CClientState__Connect = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 41 56 48 81 EC ?? ?? ?? ?? 48 8B 32");
@ -258,6 +272,7 @@ class VClientState : public IDetour
CClientState__Connect = p_CClientState__Connect.RCast<void(*)(CClientState*, connectparams_t*)>();
CClientState__Disconnect = p_CClientState__Disconnect.RCast<void(*)(CClientState*, bool)>();
CClientState__ConnectionClosing = p_CClientState__ConnectionClosing.RCast<void(*)(CClientState*, const char*)>();
CClientState__ProcessStringCmd = p_CClientState__ProcessStringCmd.RCast<bool(*)(CClientState*, NET_StringCmd*)>();
CClientState__ProcessServerTick = p_CClientState__ProcessServerTick.RCast<bool(*)(CClientState*, SVC_ServerTick*)>();
}
virtual void GetVar(void) const

View File

@ -5,6 +5,7 @@
//=============================================================================//
#include "core/stdafx.h"
#include "engine/cmd.h"
#include "clientstate.h"
#include "vengineclient_impl.h"
@ -61,4 +62,46 @@ int CEngineClient::GetLocalPlayer()
const static int index = 36;
#endif
return CallVFunc<int>(index, this);
}
}
//---------------------------------------------------------------------------------
// Purpose: execute client command
// Input : *thisptr -
// *szCmdString -
// Output :
//---------------------------------------------------------------------------------
void CEngineClient::_ClientCmd(CEngineClient* thisptr, const char* const szCmdString)
{
const bool restrictClientCommands = g_pClientState->m_bRestrictClientCommands;
const int numMarkers = 2;
if (restrictClientCommands && !Cbuf_HasRoomForExecutionMarkers(numMarkers))
{
DevWarning(eDLL_T::CLIENT, "%s: No room for %i execution markers; command \"%s\" ignored\n",
__FUNCTION__, numMarkers, szCmdString);
return;
}
if (restrictClientCommands)
{
Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE);
}
Cbuf_AddText(Cbuf_GetCurrentPlayer(), szCmdString, cmd_source_t::kCommandSrcCode);
Cbuf_AddText(Cbuf_GetCurrentPlayer(), "\n", cmd_source_t::kCommandSrcCode);
if (restrictClientCommands)
{
Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE);
}
}
void HVEngineClient::Attach() const
{
DetourAttach(&CEngineClient__ClientCmd, &CEngineClient::_ClientCmd);
}
void HVEngineClient::Detach() const
{
DetourDetach(&CEngineClient__ClientCmd, &CEngineClient::_ClientCmd);
}

View File

@ -8,10 +8,16 @@ public:
void SetRestrictClientCommands(bool bRestrict);
bool GetRestrictClientCommands() const;
int GetLocalPlayer(); // Local player index.
// Hook statics:
static void _ClientCmd(CEngineClient* thisptr, const char* const szCmdString);
};
/* ==== CVENGINECLIENT ================================================================================================================================================== */
///////////////////////////////////////////////////////////////////////////////
inline CMemory p_CEngineClient__ClientCmd;
inline void(*CEngineClient__ClientCmd)(CEngineClient* thisptr, const char* const szCmdString);
inline CMemory g_pEngineClientVFTable = nullptr;
inline CEngineClient* g_pEngineClient = nullptr;
@ -21,15 +27,20 @@ class HVEngineClient : public IDetour
virtual void GetAdr(void) const
{
LogConAdr("CEngineClient::`vftable'", g_pEngineClientVFTable.GetPtr());
LogFunAdr("CEngineClient::ClientCmd", p_CEngineClient__ClientCmd.GetPtr());
}
virtual void GetFun(void) const
{
p_CEngineClient__ClientCmd = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 80 3D ?? ?? ?? ?? ?? 48 8B DA 74 0C");
CEngineClient__ClientCmd = p_CEngineClient__ClientCmd.RCast<void(*)(CEngineClient*, const char* const)>();
}
virtual void GetFun(void) const { }
virtual void GetVar(void) const { }
virtual void GetCon(void) const
{
g_pEngineClientVFTable = g_GameDll.GetVirtualMethodTable(".?AVCEngineClient@@");
g_pEngineClient = g_pEngineClientVFTable.RCast<CEngineClient*>();
}
virtual void Attach(void) const { }
virtual void Detach(void) const { }
virtual void Attach(void) const;
virtual void Detach(void) const;
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -1,8 +1,60 @@
#include "core/stdafx.h"
#include "tier1/cmd.h"
#include "tier1/cvar.h"
#include "tier1/commandbuffer.h"
#include "engine/cmd.h"
CCommandBuffer** s_pCommandBuffer = nullptr; // array size = ECommandTarget_t::CBUF_COUNT.
LPCRITICAL_SECTION s_pCommandBufferMutex = nullptr;
//=============================================================================
// List of execution markers
//=============================================================================
CUtlVector<int>* g_pExecutionMarkers = nullptr;
//-----------------------------------------------------------------------------
// Purpose: checks if there's room left for execution markers
// Input : cExecutionMarkers -
// Output : true if there's room for execution markers, false otherwise
//-----------------------------------------------------------------------------
bool Cbuf_HasRoomForExecutionMarkers(const int cExecutionMarkers)
{
return (g_pExecutionMarkers->Count() + cExecutionMarkers) < MAX_EXECUTION_MARKERS;
}
//-----------------------------------------------------------------------------
// Purpose: adds command text at the end of the command buffer with execution markers
// Input : *pText -
// markerLeft -
// markerRight -
// Output : true if there's room for execution markers, false otherwise
//-----------------------------------------------------------------------------
bool Cbuf_AddTextWithMarkers(const char* const pText, const ECmdExecutionMarker markerLeft, const ECmdExecutionMarker markerRight)
{
if (Cbuf_HasRoomForExecutionMarkers(2))
{
Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), markerLeft);
Cbuf_AddText(Cbuf_GetCurrentPlayer(), pText, cmd_source_t::kCommandSrcCode);
Cbuf_AddExecutionMarker(Cbuf_GetCurrentPlayer(), markerRight);
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: adds command text at the end of the buffer
//-----------------------------------------------------------------------------
//void Cbuf_AddText(ECommandTarget_t eTarget, const char* pText, int nTickDelay)
//{
// LOCK_COMMAND_BUFFER();
// if (!s_pCommandBuffer[(int)eTarget]->AddText(pText, nTickDelay, cmd_source_t::kCommandSrcInvalid))
// {
// Error(eDLL_T::ENGINE, NO_ERROR, "%s: buffer overflow\n", __FUNCTION__);
// }
//}
//-----------------------------------------------------------------------------
// Purpose: Sends the entire command line over to the server
// Input : *args -
@ -19,8 +71,8 @@ bool Cmd_ForwardToServer(const CCommand* args)
if (args->ArgC() == 0)
return false;
double flStartTime = Plat_FloatTime();
int nCmdQuotaLimit = cl_quota_stringCmdsPerSecond->GetInt();
const double flStartTime = Plat_FloatTime();
const int nCmdQuotaLimit = cl_quota_stringCmdsPerSecond->GetInt();
const char* pszCmdString = nullptr;
// Special case: "cmd whatever args..." is forwarded as "whatever args...";

View File

@ -1,5 +1,17 @@
#ifndef CMD_H
#define CMD_H
#include "tier1/commandbuffer.h"
#define MAX_EXECUTION_MARKERS 2048
typedef enum
{
eCmdExecutionMarker_Enable_FCVAR_SERVER_CAN_EXECUTE = 'a',
eCmdExecutionMarker_Disable_FCVAR_SERVER_CAN_EXECUTE = 'b',
eCmdExecutionMarker_Enable_FCVAR_CLIENTCMD_CAN_EXECUTE = 'c',
eCmdExecutionMarker_Disable_FCVAR_CLIENTCMD_CAN_EXECUTE = 'd'
} ECmdExecutionMarker;
//-----------------------------------------------------------------------------
// Purpose: Returns current player calling this function
@ -11,10 +23,16 @@ FORCEINLINE ECommandTarget_t Cbuf_GetCurrentPlayer(void)
return ECommandTarget_t::CBUF_FIRST_PLAYER;
}
extern bool Cbuf_HasRoomForExecutionMarkers(const int cExecutionMarkers);
extern bool Cbuf_AddTextWithMarkers(const char* text, const ECmdExecutionMarker markerLeft, const ECmdExecutionMarker markerRight);
/* ==== COMMAND_BUFFER ================================================================================================================================================== */
inline CMemory p_Cbuf_AddText;
inline void(*Cbuf_AddText)(ECommandTarget_t eTarget, const char* pText, cmd_source_t cmdSource);
inline CMemory p_Cbuf_AddExecutionMarker;
inline void(*Cbuf_AddExecutionMarker)(ECommandTarget_t target, ECmdExecutionMarker marker);
inline CMemory p_Cbuf_Execute;
inline void(*Cbuf_Execute)(void);
@ -24,30 +42,47 @@ inline void(*v_Cmd_Dispatch)(ECommandTarget_t eTarget, const ConCommandBase* pCm
inline CMemory p_Cmd_ForwardToServer;
inline bool(*v_Cmd_ForwardToServer)(const CCommand* pCommand);
extern CCommandBuffer** s_pCommandBuffer;
extern LPCRITICAL_SECTION s_pCommandBufferMutex;
extern CUtlVector<int>* g_pExecutionMarkers;
///////////////////////////////////////////////////////////////////////////////
class VCmd : public IDetour
{
virtual void GetAdr(void) const
{
LogFunAdr("Cbuf_AddText", p_Cbuf_AddText.GetPtr());
LogFunAdr("Cbuf_AddExecutionMarker", p_Cbuf_AddExecutionMarker.GetPtr());
LogFunAdr("Cbuf_Execute", p_Cbuf_Execute.GetPtr());
LogFunAdr("Cmd_Dispatch", p_Cmd_Dispatch.GetPtr());
LogFunAdr("Cmd_ForwardToServer", p_Cmd_ForwardToServer.GetPtr());
LogVarAdr("s_CommandBuffer", reinterpret_cast<uintptr_t>(s_pCommandBuffer));
LogVarAdr("s_CommandBufferMutex", reinterpret_cast<uintptr_t>(s_pCommandBufferMutex));
LogVarAdr("g_ExecutionMarkers", reinterpret_cast<uintptr_t>(g_pExecutionMarkers));
}
virtual void GetFun(void) const
{
p_Cbuf_AddText = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??");
p_Cbuf_Execute = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??");
p_Cbuf_AddText = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??");
p_Cbuf_AddExecutionMarker = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 05 ?? ?? ?? ??");
p_Cbuf_Execute = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??");
p_Cmd_Dispatch = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B ?? 0C 49 FF C7").FollowNearCallSelf();
p_Cmd_ForwardToServer = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04");
p_Cmd_Dispatch = g_GameDll.FindPatternSIMD("E8 ?? ?? ?? ?? 8B ?? 0C 49 FF C7").FollowNearCallSelf();
p_Cmd_ForwardToServer = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04");
Cbuf_AddText = p_Cbuf_AddText.RCast<void (*)(ECommandTarget_t, const char*, cmd_source_t)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??*/
Cbuf_Execute = p_Cbuf_Execute.RCast<void (*)(void)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??*/
v_Cmd_Dispatch = p_Cmd_Dispatch.RCast<void (*)(ECommandTarget_t, const ConCommandBase*, const CCommand*, bool)>();
v_Cmd_ForwardToServer = p_Cmd_ForwardToServer.RCast<bool (*)(const CCommand*)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04*/
Cbuf_AddText = p_Cbuf_AddText.RCast<void (*)(ECommandTarget_t, const char*, cmd_source_t)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 48 63 D9 41 8B F8 48 8D 0D ?? ?? ?? ?? 48 8B F2 FF 15 ?? ?? ?? ?? 48 8D 05 ?? ?? ?? ?? 41 B9 ?? ?? ?? ??*/
Cbuf_AddExecutionMarker = p_Cbuf_AddExecutionMarker.RCast<void(*)(ECommandTarget_t, ECmdExecutionMarker)>();
Cbuf_Execute = p_Cbuf_Execute.RCast<void (*)(void)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 FF 15 ?? ?? ?? ??*/
v_Cmd_Dispatch = p_Cmd_Dispatch.RCast<void (*)(ECommandTarget_t, const ConCommandBase*, const CCommand*, bool)>();
v_Cmd_ForwardToServer = p_Cmd_ForwardToServer.RCast<bool (*)(const CCommand*)>(); /*48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 44 8B 59 04*/
}
virtual void GetVar(void) const
{
s_pCommandBuffer = p_Cbuf_AddText.FindPattern("48 8D 05").ResolveRelativeAddressSelf(3, 7).RCast<CCommandBuffer**>();
s_pCommandBufferMutex = p_Cbuf_AddText.FindPattern("48 8D 0D").ResolveRelativeAddressSelf(3, 7).RCast<LPCRITICAL_SECTION>();
g_pExecutionMarkers = p_Cbuf_AddExecutionMarker.FindPattern("48 8B 0D").ResolveRelativeAddressSelf(3, 7).RCast<CUtlVector<int>*>();
}
virtual void GetVar(void) const { }
virtual void GetCon(void) const { }
virtual void Attach(void) const;
virtual void Detach(void) const;