(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
This commit is contained in:
rexx 2023-04-29 11:11:10 +01:00
parent 6b1d02dfea
commit dca27475ec
9 changed files with 167 additions and 3 deletions

View File

@ -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<void**>(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<bool>(NetMessageVtbl::Process, this);
};
virtual bool ReadFromBuffer(bf_read* buffer)
{
return CallVFunc<bool>(NetMessageVtbl::ReadFromBuffer, this, buffer);
}
virtual bool WriteToBuffer(bf_write* buffer)
{
return CallVFunc<bool>(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<uintptr_t>(g_pSVC_Print_VFTable));
LogConAdr("SVC_UserMessage::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_UserMessage_VFTable));
LogConAdr("SVC_ServerTick::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_ServerTick_VFTable));
LogConAdr("SVC_VoiceData::`vftable'", reinterpret_cast<uintptr_t>(g_pSVC_VoiceData_VFTable));
LogConAdr("Base_CmdKeyValues::`vftable'", reinterpret_cast<uintptr_t>(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;

View File

@ -22,6 +22,14 @@ CClient* CClient::GetClient(int nIndex) const
(reinterpret_cast<uintptr_t>(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
//---------------------------------------------------------------------------------

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -4,6 +4,8 @@
///////////////////////////////////////////////////////////////////////////////
class CClient;
/* ==== SV_MAIN ======================================================================================================================================================= */
inline CMemory p_SV_InitGameDLL;
inline auto SV_InitGameDLL = p_SV_InitGameDLL.RCast<void(*)(void)>();
@ -17,6 +19,9 @@ inline auto SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
inline CMemory p_CGameServer__SpawnServer;
inline auto CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast<bool(*)(void* thisptr, const char* pszMapName, const char* pszMapGroupName)>();
inline CMemory p_SV_BroadcastVoiceData;
inline auto v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast<void(__fastcall*)(CClient* cl, int nBytes, char* data)>();
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<void(*)(void)>();
SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast<void(*)(void)>();
SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast<bool(*)(void*, const char*, const char*)>();
v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast<void(__fastcall*)(CClient* cl, int nBytes, char* data)>();
}
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<bool*>();
}
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;
};
///////////////////////////////////////////////////////////////////////////////

View File

@ -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;

View File

@ -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);