From dca27475ecef2f0db3a6f065e6825002d638a787 Mon Sep 17 00:00:00 2001 From: rexx <67599507+r-ex@users.noreply.github.com> Date: Sat, 29 Apr 2023 11:11:10 +0100 Subject: [PATCH] (re)implement sv_alltalk Rebuilds SV_BroadcastVoiceData with an additional check of "sv_alltalk" cvar to disable team checking, allowing for servers to enable global voice chat --- r5dev/common/netmessages.h | 66 +++++++++++++++++++++++++++++++++ r5dev/engine/client/client.cpp | 8 ++++ r5dev/engine/client/client.h | 4 +- r5dev/engine/net_chan.cpp | 8 ++++ r5dev/engine/net_chan.h | 2 + r5dev/engine/server/sv_main.cpp | 58 +++++++++++++++++++++++++++++ r5dev/engine/server/sv_main.h | 12 +++++- r5dev/public/tier1/cvar.h | 4 ++ r5dev/tier1/cvar.cpp | 8 ++++ 9 files changed, 167 insertions(+), 3 deletions(-) diff --git a/r5dev/common/netmessages.h b/r5dev/common/netmessages.h index 18cb42dc..e418f36d 100644 --- a/r5dev/common/netmessages.h +++ b/r5dev/common/netmessages.h @@ -46,6 +46,11 @@ inline void* g_pSVC_UserMessage_VFTable = nullptr; //------------------------------------------------------------------------- inline void* g_pSVC_ServerTick_VFTable = nullptr; +//------------------------------------------------------------------------- +// SVC_VoiceData +//------------------------------------------------------------------------- +inline void* g_pSVC_VoiceData_VFTable = nullptr; + //------------------------------------------------------------------------- // Base_CmdKeyValues //------------------------------------------------------------------------- @@ -197,6 +202,65 @@ public: nettick_t m_NetTick; }; +struct SVC_VoiceData : public CNetMessage +{ +public: + SVC_VoiceData() = default; + SVC_VoiceData(int senderClient, int nBytes, char* data) + { + void** pVFTable = reinterpret_cast(this); + *pVFTable = g_pSVC_VoiceData_VFTable; + + m_bReliable = false; + m_NetChannel = nullptr; + + m_nFromClient = senderClient; + m_nLength = nBytes; // length in bits + m_DataOut = data; + + m_nGroup = 2; // must be set to 2 to avoid being copied into replay buffer + }; + + virtual ~SVC_VoiceData() {}; + + virtual void SetNetChannel(CNetChan* netchan) { m_NetChannel = netchan; } + virtual void SetReliable(bool state) { m_bReliable = state; }; + + virtual bool Process(void) + { + return CallVFunc(NetMessageVtbl::Process, this); + }; + virtual bool ReadFromBuffer(bf_read* buffer) + { + return CallVFunc(NetMessageVtbl::ReadFromBuffer, this, buffer); + } + virtual bool WriteToBuffer(bf_write* buffer) + { + return CallVFunc(NetMessageVtbl::WriteToBuffer, this, buffer); + } + + virtual bool IsReliable(void) const { return m_bReliable; }; + + virtual int GetGroup(void) const { return m_nGroup; }; + virtual int GetType(void) const { return 14; }; + virtual const char* GetName(void) const { return "svc_VoiceData"; }; + virtual CNetChan* GetNetChannel(void) const { return m_NetChannel; }; + virtual const char* ToString(void) const + { + static char szBuf[4096]; + V_snprintf(szBuf, sizeof(szBuf), "%s: client %i, bytes %i", this->GetName(), m_nFromClient, ((m_nLength + 7) >> 3)); + + return szBuf; + }; + virtual size_t GetSize(void) const { return sizeof(SVC_VoiceData); }; + + int m_nFromClient; + int m_nLength; + bf_read m_DataIn; + void* m_DataOut; +}; + + /////////////////////////////////////////////////////////////////////////////////////// // Client messages: /////////////////////////////////////////////////////////////////////////////////////// @@ -249,6 +313,7 @@ class V_NetMessages : public IDetour LogConAdr("SVC_Print::`vftable'", reinterpret_cast(g_pSVC_Print_VFTable)); LogConAdr("SVC_UserMessage::`vftable'", reinterpret_cast(g_pSVC_UserMessage_VFTable)); LogConAdr("SVC_ServerTick::`vftable'", reinterpret_cast(g_pSVC_ServerTick_VFTable)); + LogConAdr("SVC_VoiceData::`vftable'", reinterpret_cast(g_pSVC_VoiceData_VFTable)); LogConAdr("Base_CmdKeyValues::`vftable'", reinterpret_cast(g_pBase_CmdKeyValues_VFTable)); LogFunAdr("MM_Heartbeat::ToString", MM_Heartbeat__ToString.GetPtr()); } @@ -264,6 +329,7 @@ class V_NetMessages : public IDetour g_pSVC_Print_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_Print@@"); g_pSVC_UserMessage_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_UserMessage@@"); g_pSVC_ServerTick_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_ServerTick@@"); + g_pSVC_VoiceData_VFTable = g_GameDll.GetVirtualMethodTable(".?AVSVC_VoiceData@@"); g_pBase_CmdKeyValues_VFTable = g_GameDll.GetVirtualMethodTable(".?AVBase_CmdKeyValues@@"); } virtual void Attach(void) const; diff --git a/r5dev/engine/client/client.cpp b/r5dev/engine/client/client.cpp index e479851e..a3a228e1 100644 --- a/r5dev/engine/client/client.cpp +++ b/r5dev/engine/client/client.cpp @@ -22,6 +22,14 @@ CClient* CClient::GetClient(int nIndex) const (reinterpret_cast(g_pClient) + (nIndex * sizeof(CClient)))); } +//--------------------------------------------------------------------------------- +// Purpose: gets the client's team number +//--------------------------------------------------------------------------------- +__int64 CClient::GetTeamNum() const +{ + return m_iTeamNum; +} + //--------------------------------------------------------------------------------- // Purpose: gets the handle of this client //--------------------------------------------------------------------------------- diff --git a/r5dev/engine/client/client.h b/r5dev/engine/client/client.h index 8545b8fa..50a9ce3e 100644 --- a/r5dev/engine/client/client.h +++ b/r5dev/engine/client/client.h @@ -62,6 +62,7 @@ class CClient : IClientMessageHandler, INetChannelHandler { public: CClient* GetClient(int nIndex) const; + __int64 GetTeamNum() const; edict_t GetHandle(void) const; uint32_t GetUserID(void) const; uint64_t GetNucleusID(void) const; @@ -104,7 +105,8 @@ private: char m_szClientName[256]; //0x0116 char pad_0015[258]; //0x0216 int m_nCommandTick; //0x0318 - char pad_031C[68]; //0x031C + char pad_031C[60]; //0x031C + __int64 m_iTeamNum; //0x0258 KeyValues* m_ConVars; //0x0360 char pad_0368[8]; //0x0368 CServer* m_pServer; //0x0370 diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp index 43c1369e..59fef3a9 100644 --- a/r5dev/engine/net_chan.cpp +++ b/r5dev/engine/net_chan.cpp @@ -178,6 +178,14 @@ int CNetChan::GetSocket(void) const return this->m_Socket; } +//----------------------------------------------------------------------------- +// Purpose: gets a const reference to m_StreamVoice +//----------------------------------------------------------------------------- +const bf_write& CNetChan::GetStreamVoice(void) const +{ + return this->m_StreamVoice; +} + //----------------------------------------------------------------------------- // Purpose: checks if the reliable stream is overflowed // Output : true if overflowed, false otherwise diff --git a/r5dev/engine/net_chan.h b/r5dev/engine/net_chan.h index 0e829e02..ea110911 100644 --- a/r5dev/engine/net_chan.h +++ b/r5dev/engine/net_chan.h @@ -100,6 +100,8 @@ public: double GetTimeConnected(void) const; int GetSocket(void) const; + const bf_write& GetStreamVoice(void) const; + bool IsOverflowed(void) const; void Clear(bool bStopProcessing); diff --git a/r5dev/engine/server/sv_main.cpp b/r5dev/engine/server/sv_main.cpp index f01d88b5..c40a3238 100644 --- a/r5dev/engine/server/sv_main.cpp +++ b/r5dev/engine/server/sv_main.cpp @@ -4,6 +4,9 @@ #include "engine/server/sv_main.h" #include "networksystem/pylon.h" #include "networksystem/bansystem.h" +#include "engine/client/client.h" +#include "tier1/cvar.h" +#include "server.h" //----------------------------------------------------------------------------- // Purpose: checks if particular client is banned on the comp server @@ -26,4 +29,59 @@ void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID) } } +void SV_BroadcastVoiceData(CClient* cl, int nBytes, char* data) +{ + if (!sv_voiceenable->GetBool()) + return; + + if (g_ServerGlobalVariables->m_nMaxClients <= 0) + return; + + SVC_VoiceData voiceData(cl->GetUserID(), nBytes, data); + + for (int i = 0; i < g_ServerGlobalVariables->m_nMaxClients; i++) + { + CClient* pClient = g_pClient->GetClient(i); + + if (!pClient) + continue; + + // is this client fully connected + if (pClient->GetSignonState() != SIGNONSTATE::SIGNONSTATE_FULL) + continue; + + // is this client the sender + if (pClient == cl && !sv_voiceEcho->GetBool()) + continue; + + // is this client on the sender's team + if (pClient->GetTeamNum() != cl->GetTeamNum() && !sv_alltalk->GetBool()) + continue; + + // there's also supposed to be some xplat checks here + // but since r5r is only on PC, there's no point in implementing them here + + CNetChan* pNetChan = pClient->GetNetChan(); + + if (!pNetChan) + continue; + + // if voice stream has enough space for new data + if (pNetChan->GetStreamVoice().GetNumBitsLeft() >= 8 * nBytes + 96) + pClient->SendNetMsg(&voiceData, false, false, true); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void HSV_Main::Attach(void) const +{ + DetourAttach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData); +} + +void HSV_Main::Detach(void) const +{ + DetourDetach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData); +} + /////////////////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/r5dev/engine/server/sv_main.h b/r5dev/engine/server/sv_main.h index 0beff5d5..aec1bceb 100644 --- a/r5dev/engine/server/sv_main.h +++ b/r5dev/engine/server/sv_main.h @@ -4,6 +4,8 @@ /////////////////////////////////////////////////////////////////////////////// +class CClient; + /* ==== SV_MAIN ======================================================================================================================================================= */ inline CMemory p_SV_InitGameDLL; inline auto SV_InitGameDLL = p_SV_InitGameDLL.RCast(); @@ -17,6 +19,9 @@ inline auto SV_CreateBaseline = p_SV_CreateBaseline.RCast(); inline CMemory p_CGameServer__SpawnServer; inline auto CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); +inline CMemory p_SV_BroadcastVoiceData; +inline auto v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast(); + inline bool* s_bIsDedicated = nullptr; /////////////////////////////////////////////////////////////////////////////// @@ -46,10 +51,13 @@ class HSV_Main : public IDetour p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("48 8B C4 53 55 56 57 41 54 41 55 41 57"); // 0x140312D80 // 48 8B C4 53 55 56 57 41 54 41 55 41 57 // #endif + p_SV_BroadcastVoiceData = g_GameDll.FindPatternSIMD("4C 8B DC 56 48 81 EC ? ? ? ? 80 3D ? ? ? ? ? "); + SV_InitGameDLL = p_SV_InitGameDLL.RCast(); SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast(); SV_CreateBaseline = p_SV_CreateBaseline.RCast(); CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast(); + v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast(); } virtual void GetVar(void) const { @@ -57,7 +65,7 @@ class HSV_Main : public IDetour .FindPatternSelf("40 38 3D", CMemory::Direction::DOWN).ResolveRelativeAddressSelf(0x3, 0x7).RCast(); } virtual void GetCon(void) const { } - virtual void Attach(void) const { } - virtual void Detach(void) const { } + virtual void Attach(void) const; + virtual void Detach(void) const; }; /////////////////////////////////////////////////////////////////////////////// diff --git a/r5dev/public/tier1/cvar.h b/r5dev/public/tier1/cvar.h index 3b905f1d..735745ea 100644 --- a/r5dev/public/tier1/cvar.h +++ b/r5dev/public/tier1/cvar.h @@ -106,6 +106,10 @@ extern ConVar* sv_validatePersonaName; extern ConVar* sv_minPersonaNameLength; extern ConVar* sv_maxPersonaNameLength; +extern ConVar* sv_voiceEcho; +extern ConVar* sv_voiceenable; +extern ConVar* sv_alltalk; + //#ifdef DEDICATED extern ConVar* sv_rcon_debug; extern ConVar* sv_rcon_sendlogs; diff --git a/r5dev/tier1/cvar.cpp b/r5dev/tier1/cvar.cpp index acbf8d6e..66f9ae5a 100644 --- a/r5dev/tier1/cvar.cpp +++ b/r5dev/tier1/cvar.cpp @@ -101,6 +101,10 @@ ConVar* sv_validatePersonaName = nullptr; ConVar* sv_minPersonaNameLength = nullptr; ConVar* sv_maxPersonaNameLength = nullptr; +ConVar* sv_voiceEcho = nullptr; +ConVar* sv_voiceenable = nullptr; +ConVar* sv_alltalk = nullptr; + //#ifdef DEDICATED ConVar* sv_rcon_debug = nullptr; ConVar* sv_rcon_sendlogs = nullptr; @@ -524,6 +528,10 @@ void ConVar::InitShipped(void) sv_showhitboxes = g_pCVar->FindVar("sv_showhitboxes"); sv_forceChatToTeamOnly = g_pCVar->FindVar("sv_forceChatToTeamOnly"); + sv_voiceenable = g_pCVar->FindVar("sv_voiceenable"); + sv_voiceEcho = g_pCVar->FindVar("sv_voiceEcho"); + sv_alltalk = g_pCVar->FindVar("sv_alltalk"); + sv_showhitboxes->SetMin(-1); // Allow user to go over each entity manually without going out of bounds. sv_showhitboxes->SetMax(NUM_ENT_ENTRIES - 1);