Engine: add new netmsg 'SVC_SetClassVar'

Netmessage SVC_SetClassVar allows the server to change class settings securely on the client. This was implemented due to popular demand, and previous approaches using a combination of ClientCommands (Cbuf and NET_StringCmd) were deemed insecure and unreliable.
This commit is contained in:
Kawe Mazidjatari 2024-09-25 20:08:28 +02:00
parent 3b1622af95
commit b11d7e3c9b
4 changed files with 93 additions and 2 deletions

View File

@ -4,6 +4,8 @@ inline bool(*v_SetupGamemode)(const char* pszPlayList);
/* ==== CONCOMMANDCALLBACK ============================================================================================================================================== */
inline void(*v__Cmd_Exec_f)(const CCommand& args);
inline bool(*v__setClassVarServer_f)(const CCommand& args);
inline bool(*v__setClassVarClient_f)(const CCommand& args);
#ifndef DEDICATED
inline void(*v__UIScript_Reset_f)();
#endif // !DEDICATED
@ -41,6 +43,8 @@ class VCallback : public IDetour
{
LogFunAdr("SetupGamemode", v_SetupGamemode);
LogFunAdr("Cmd_Exec_f", v__Cmd_Exec_f);
LogFunAdr("SetClassVarServer_f", v__setClassVarServer_f);
LogFunAdr("SetClassVarClient_f", v__setClassVarClient_f);
#ifndef DEDICATED
LogFunAdr("UIScript_Reset_f", v__UIScript_Reset_f);
#endif // !DEDICATED
@ -49,6 +53,8 @@ class VCallback : public IDetour
{
g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 48 8B D9 48 C7 C0 ?? ?? ?? ??").GetPtr(v_SetupGamemode);
g_GameDll.FindPatternSIMD("40 55 53 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B D9").GetPtr(v__Cmd_Exec_f);
g_GameDll.FindPatternSIMD("41 56 48 83 EC ?? 8B 05 ?? ?? ?? ?? 4C 8B F1 FF C0").GetPtr(v__setClassVarServer_f);
g_GameDll.FindPatternSIMD("4C 8B DC 57 48 81 EC ?? ?? ?? ?? 8B 05").GetPtr(v__setClassVarClient_f);
#ifndef DEDICATED
g_GameDll.FindPatternSIMD("40 55 41 54 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 45 33 E4 48 8D 0D").GetPtr(v__UIScript_Reset_f);
#endif // !DEDICATED

View File

@ -12,6 +12,7 @@
#include "tier1/cvar.h"
#include "engine/net.h"
#include "common/netmessages.h"
#include "common/callback.h"
#include "game/shared/usermessages.h"
///////////////////////////////////////////////////////////////////////////////////
@ -64,6 +65,37 @@ bool SVC_UserMessage::ProcessImpl()
return SVC_UserMessage_Process(this); // Need to return original.
}
///////////////////////////////////////////////////////////////////////////////////
// Net message to change class settings vars on the client
///////////////////////////////////////////////////////////////////////////////////
bool SVC_SetClassVar::ReadFromBuffer(bf_read* buffer)
{
const bool set = buffer->ReadString(m_szSetting, sizeof(m_szSetting));
const bool var = buffer->ReadString(m_szVariable, sizeof(m_szVariable));
return set && var;
}
bool SVC_SetClassVar::WriteToBuffer(bf_write* buffer)
{
const bool set = buffer->WriteString(m_szSetting);
const bool var = buffer->WriteString(m_szVariable);
return set && var;
}
bool SVC_SetClassVar::Process(void)
{
const char* pArgs[3] = {
"_setClassVarClient",
m_szSetting,
m_szVariable
};
CCommand command((int)V_ARRAYSIZE(pArgs), pArgs, cmd_source_t::kCommandSrcCode);
v__setClassVarClient_f(command);
return true;
}
///////////////////////////////////////////////////////////////////////////////////
// Below functions are hooked as playlist overrides can be abused from the client.
// The client could basically manage the server's playlists. Only allow read/write
@ -122,7 +154,7 @@ bool Base_CmdKeyValues::WriteToBufferImpl(Base_CmdKeyValues* thisptr, bf_write*
// determine whether or not the message should be copied into the replay buffer,
// regardless of the 'CNetMessage::m_Group' type.
///////////////////////////////////////////////////////////////////////////////////
bool ShouldReplayMessage(const CNetMessage* msg)
bool ShouldReplayMessage(const CNetMessage* msg) // todo(amos): rename to 'CanReplayMessage'
{
switch (msg->GetType())
{

View File

@ -162,6 +162,15 @@ enum NetMessageType
clc_AntiCheat = 63,
clc_AntiCheatChallenge = 64,
clc_GamepadMsg = 65,
// NOTE: if you make new netmessages, the order should be as follows:
// - NET (messages shared between server & client).
// - SVC (messages originating from the server).
// - CLC (messages originating from the client.
// Reorder message indices to retain consistency here.
// --------------------------------------------------------------------
// From here on, SDK netmessages are enumerated.
svc_SetClassVar = 66,
};
//-------------------------------------------------------------------------
@ -176,10 +185,16 @@ enum NetMessageGroup
class CNetMessage : public INetMessage
{
public:
virtual void SetNetChannel(CNetChan* netchan) { m_NetChannel = netchan; }
virtual void SetReliable(bool state) { m_bReliable = state; }
virtual bool IsReliable(void) const { return m_bReliable; }
virtual int GetGroup(void) const { return m_nGroup; }
virtual CNetChan* GetNetChannel(void) const { return m_NetChannel; }
int m_nGroup;
bool m_bReliable;
CNetChan* m_NetChannel;
INetMessageHandler* m_pMessageHandler;
INetChannelHandler* m_pMessageHandler;
};
///////////////////////////////////////////////////////////////////////////////////////
@ -445,6 +460,43 @@ private:
bf_write m_DataOut;
};
class SVC_SetClassVar : public CNetMessage
{
public:
SVC_SetClassVar() = default;
SVC_SetClassVar(const char* setting, const char* var)
{
V_strncpy(m_szSetting, setting, sizeof(m_szSetting));
V_strncpy(m_szVariable, var, sizeof(m_szVariable));
m_szSetting[sizeof(m_szSetting) - 1] = '\0';
m_szVariable[sizeof(m_szVariable) - 1] = '\0';
m_nGroup = 2; // must be set to 2 to avoid being copied into replay buffer
}
virtual bool ReadFromBuffer(bf_read* buffer);
virtual bool WriteToBuffer(bf_write* buffer);
virtual bool Process(void);
virtual int GetType(void) const { return svc_SetClassVar; };
virtual const char* GetName(void) const { return "svc_SetClassVar"; };
virtual const char* ToString(void) const
{
static char szBuf[4096];
V_snprintf(szBuf, sizeof(szBuf), "%s: setting \"%s\", variable \"%s\"", this->GetName(), m_szSetting, m_szVariable);
return szBuf;
};
virtual size_t GetSize(void) const { return sizeof(SVC_SetClassVar); }
char m_szSetting[128];
char m_szVariable[128];
};
///////////////////////////////////////////////////////////////////////////////////////
// Client messages:
///////////////////////////////////////////////////////////////////////////////////////

View File

@ -509,6 +509,7 @@ void CClientState::Reconnect()
//---------------------------------------------------------------------------------
void CClientState::RegisterNetMsgs(CNetChan* chan)
{
REGISTER_SVC_MSG(SetClassSettingsVar);
}
void VClientState::Detour(const bool bAttach) const