From 421836b89f86af1ac90e5c02cf8be2e65bed42d7 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 13 Aug 2022 17:19:45 +0200 Subject: [PATCH 1/3] [Voice] Voice chat --- src/Components/Loader.cpp | 1 + src/Components/Loader.hpp | 1 + src/Components/Modules/Auth.cpp | 2 +- src/Components/Modules/ClanTags.hpp | 2 - src/Components/Modules/Console.cpp | 2 + src/Components/Modules/Network.cpp | 4 +- src/Components/Modules/Security.cpp | 4 +- src/Components/Modules/Stats.cpp | 8 +- src/Components/Modules/Theatre.cpp | 4 +- src/Components/Modules/Voice.cpp | 268 ++++++++++++++++++++++++++++ src/Components/Modules/Voice.hpp | 34 ++++ src/Game/Dvars.cpp | 5 + src/Game/Dvars.hpp | 5 + src/Game/Functions.cpp | 42 ++++- src/Game/Functions.hpp | 39 +++- src/Game/Structs.hpp | 47 +++-- 16 files changed, 428 insertions(+), 40 deletions(-) create mode 100644 src/Components/Modules/Voice.cpp create mode 100644 src/Components/Modules/Voice.hpp diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index c1163385..623dd9cc 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -112,6 +112,7 @@ namespace Components Loader::Register(new Ceg()); Loader::Register(new UserInfo()); Loader::Register(new Events()); + Loader::Register(new Voice()); Loader::Register(new GSC()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 67bcc26f..b8a494a9 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -143,5 +143,6 @@ namespace Components #include "Modules/Ceg.hpp" #include "Modules/UserInfo.hpp" #include "Modules/Events.hpp" +#include "Modules/Voice.hpp" #include "Modules/GSC/GSC.hpp" diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index 01f87c59..47c86619 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -130,7 +130,7 @@ namespace Components // Parse proto data Proto::Auth::Connect connectData; - if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(&msg->data[12], msg->cursize - 12))) + if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(reinterpret_cast(&msg->data[12]), msg->cursize - 12))) { Network::Send(address, "error\nInvalid connect packet!"); return; diff --git a/src/Components/Modules/ClanTags.hpp b/src/Components/Modules/ClanTags.hpp index 1893d620..7f3aec77 100644 --- a/src/Components/Modules/ClanTags.hpp +++ b/src/Components/Modules/ClanTags.hpp @@ -28,8 +28,6 @@ namespace Components static void Dvar_InfoString_Stub(char* s, const char* key, const char* value); - static void SetCachedPlayerData(int clientNum); - static void ClientUserinfoChanged(const char* s, int clientNum); static void ClientUserinfoChanged_Stub(); diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index c46f5f17..4b02bcb9 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -568,6 +568,8 @@ namespace Components Console::Console() { + AssertOffset(Game::clientUIActive_t, connectionState, 0x9B8); + // Console '%s: %s> ' string Utils::Hook::Set(0x5A44B4, "IW4x MP: " VERSION "> "); diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index f434dc19..a9c3e2dc 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -292,7 +292,7 @@ namespace Components return false; } - const std::string data(message->data + offset, message->cursize - offset); + const std::string data(reinterpret_cast(message->data) + offset, message->cursize - offset); Address address_ = address; handler->second(address_, data); @@ -370,6 +370,8 @@ namespace Components Utils::Hook::Set(0x5AA5B6, 0xEB); // CL_SteamServerAuth Utils::Hook::Set(0x5AA69F, 0xEB); // echo Utils::Hook::Set(0x5AAA82, 0xEB); // SP + Utils::Hook::Set(0x5A9F18, 0xEB); // CL_VoiceConnectionTestPacket + Utils::Hook::Set(0x5A9FF3, 0xEB); // CL_HandleRelayPacket Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data) { diff --git a/src/Components/Modules/Security.cpp b/src/Components/Modules/Security.cpp index 847f844b..07193220 100644 --- a/src/Components/Modules/Security.cpp +++ b/src/Components/Modules/Security.cpp @@ -102,8 +102,8 @@ namespace Components void Security::NET_DeferPacketToClientStub(Game::netadr_t* net_from, Game::msg_t* net_message) { - assert(net_from != nullptr); - assert(net_message != nullptr); + assert(net_from); + assert(net_message); if (static_cast(net_message->cursize) >= sizeof(Game::DeferredMsg::data)) { diff --git a/src/Components/Modules/Stats.cpp b/src/Components/Modules/Stats.cpp index ca710831..9dce4f33 100644 --- a/src/Components/Modules/Stats.cpp +++ b/src/Components/Modules/Stats.cpp @@ -24,10 +24,8 @@ namespace Components Game::Com_Printf(0, "Sending stat packet %i to server.\n", i); // alloc - Game::msg_t msg; - char buffer[2048]; - ZeroMemory(&msg, sizeof(msg)); - ZeroMemory(&buffer, sizeof(buffer)); + Game::msg_t msg{}; + unsigned char buffer[2048]{}; // init Game::MSG_Init(&msg, buffer, sizeof(buffer)); @@ -53,7 +51,7 @@ namespace Components } // send statpacket - Network::SendRaw(Game::NS_CLIENT1, *reinterpret_cast(0xA1E888), std::string(msg.data, msg.cursize)); + Network::SendRaw(Game::NS_CLIENT1, *reinterpret_cast(0xA1E888), std::string(reinterpret_cast(msg.data), msg.cursize)); } } } diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index d2ce0a30..912d94d5 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -47,7 +47,7 @@ namespace Components void Theatre::WriteBaseline() { - static char bufData[131072]; + static unsigned char bufData[131072]; static char cmpData[131072]; Game::msg_t buf; @@ -56,7 +56,7 @@ namespace Components Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff); Game::MSG_WriteByte(&buf, 6); - int compressedSize = Game::MSG_WriteBitsCompress(false, buf.data, cmpData, buf.cursize); + int compressedSize = Game::MSG_WriteBitsCompress(false, reinterpret_cast(buf.data), cmpData, buf.cursize); int fileCompressedSize = compressedSize + 4; int byte8 = 8; diff --git a/src/Components/Modules/Voice.cpp b/src/Components/Modules/Voice.cpp new file mode 100644 index 00000000..adf233b4 --- /dev/null +++ b/src/Components/Modules/Voice.cpp @@ -0,0 +1,268 @@ +#include + +namespace Components +{ + Game::VoicePacket_t Voice::voicePackets[Game::MAX_CLIENTS][MAX_SERVER_QUEUED_VOICE_PACKETS]; + int Voice::voicePacketCount[Game::MAX_CLIENTS]; + + const Game::dvar_t* Voice::sv_voice; + + bool Voice::SV_VoiceEnabled() + { + return sv_voice->current.enabled; + } + + void Voice::SV_WriteVoiceDataToClient(const int clientNum, Game::msg_t* msg) + { + assert(voicePacketCount[clientNum] >= 0); + assert(voicePacketCount[clientNum] <= MAX_SERVER_QUEUED_VOICE_PACKETS); + + Game::MSG_WriteByte(msg, voicePacketCount[clientNum]); + for (auto packet = 0; packet < voicePacketCount[clientNum]; ++packet) + { + Game::MSG_WriteByte(msg, voicePackets[clientNum][packet].talker); + + assert(voicePackets[clientNum][packet].dataSize < (2 << 15)); + + Game::MSG_WriteByte(msg, voicePackets[clientNum][packet].dataSize); + Game::MSG_WriteData(msg, voicePackets[clientNum][packet].data, voicePackets[clientNum][packet].dataSize); + } + + assert(!msg->overflowed); + } + + void Voice::SV_SendClientVoiceData(Game::client_t* client) + { + const auto msg_buf = std::make_unique(0x10000); + Game::msg_t msg{}; + const auto clientNum = client - Game::svs_clients; + + assert(voicePacketCount[clientNum] >= 0); + + if (client->state == Game::CS_ACTIVE && voicePacketCount[clientNum]) + { + Game::MSG_Init(&msg, msg_buf.get(), 0x10000); + + assert(msg.cursize == 0); + assert(msg.bit == 0); + + Game::MSG_WriteString(&msg, "v"); + SV_WriteVoiceDataToClient(clientNum, &msg); + + if (msg.overflowed) + { + Logger::Warning(Game::CON_CHANNEL_SERVER, "WARNING: voice msg overflowed for {}\n", client->name); + } + else + { + Game::NET_OutOfBandVoiceData(Game::NS_SERVER, client->netchan.remoteAddress, msg.data, msg.cursize, true); + voicePacketCount[clientNum] = 0; + } + } + } + + void Voice::SV_SendClientMessages_Stub(Game::client_t* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf) + { + // SV_EndClientSnapshot + Utils::Hook::Call(0x4F5300)(client, msg, snapshotMsgBuf); + + SV_SendClientVoiceData(client); + } + + bool Voice::OnSameTeam(Game::gentity_s* ent1, Game::gentity_s* ent2) + { + if (!ent1->client || !ent2->client) + { + return false; + } + if (ent1->client->sess.cs.team) + { + return ent1->client->sess.cs.team == ent2->client->sess.cs.team; + } + return false; + } + + void Voice::SV_QueueVoicePacket(int talkerNum, int clientNum, Game::VoicePacket_t* voicePacket) + { + assert(talkerNum >= 0); + assert(clientNum >= 0); + assert(talkerNum < (*Game::sv_maxclients)->current.integer); + assert(clientNum < (*Game::sv_maxclients)->current.integer); + + if (voicePacketCount[clientNum] < MAX_SERVER_QUEUED_VOICE_PACKETS) + { + voicePackets[clientNum][voicePacketCount[clientNum]].dataSize = voicePacket->dataSize; + std::memcpy(voicePackets[clientNum][voicePacketCount[clientNum]].data, voicePacket->data, voicePacket->dataSize); + + assert(talkerNum == static_cast(talkerNum)); + voicePackets[clientNum][voicePacketCount[clientNum]].talker = static_cast(talkerNum); + ++voicePacketCount[clientNum]; + } + } + + void Voice::G_BroadcastVoice(Game::gentity_s* talker, Game::VoicePacket_t* voicePacket) + { + for (auto otherPlayer = 0; otherPlayer < (*Game::sv_maxclients)->current.integer; ++otherPlayer) + { + auto* ent = &Game::g_entities[otherPlayer]; + auto* client = ent->client; + + if (ent->r.isInUse && client && (client->sess.sessionState == Game::SESS_STATE_INTERMISSION || OnSameTeam(talker, ent) || talker->client->sess.cs.team == Game::TEAM_FREE) && + (ent->client->sess.sessionState == talker->client->sess.sessionState || (ent->client->sess.sessionState == Game::SESS_STATE_DEAD || talker->client->sess.sessionState == Game::SESS_STATE_DEAD) && + (*Game::g_deadChat)->current.enabled) && (talker != ent)) + { + SV_QueueVoicePacket(talker->s.number, otherPlayer, voicePacket); + } + } + } + + void Voice::SV_UserVoice(Game::client_t* cl, Game::msg_t* msg) + { + Game::VoicePacket_t voicePacket{}; + + if (!SV_VoiceEnabled()) + { + return; + } + + const auto packetCount = Game::MSG_ReadByte(msg); + + assert(cl->gentity); + + for (int packet = 0; packet < packetCount; ++packet) + { + voicePacket.dataSize = Game::MSG_ReadByte(msg); + if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA) + { + Logger::Print(Game::CON_CHANNEL_SERVER, "Received invalid voice packet of size {} from {}\n", voicePacket.dataSize, cl->name); + return; + } + + assert(voicePacket.dataSize <= MAX_VOICE_PACKET_DATA); + assert(msg->data); + assert(voicePacket.data); + + Game::MSG_ReadData(msg, voicePacket.data, voicePacket.dataSize); + G_BroadcastVoice(cl->gentity, &voicePacket); + } + } + + void Voice::SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg) + { + auto qport = Game::MSG_ReadShort(msg); + auto* cl = Game::SV_FindClientByAddress(from, qport, 0); + if (!cl || cl->state == Game::CS_ZOMBIE) + { + return; + } + + if (cl->state == Game::CS_ACTIVE) + { + assert(cl->gentity); + SV_UserVoice(cl, msg); + } + } + + void Voice::CL_WriteVoicePacket_Hk(int localClientNum) + { + const auto connstate = Game::CL_GetLocalClientConnectionState(localClientNum); + const auto clc = Game::CL_GetLocalClientConnection(localClientNum); + const auto* vc = Game::CL_GetLocalClientVoiceCommunication(localClientNum); + if (clc->demoplaying || (connstate < Game::CA_LOADING)) + { + return; + } + + unsigned char voicePacketBuf[0x800]{}; + Game::msg_t msg{}; + + Game::MSG_Init(&msg, voicePacketBuf, sizeof(voicePacketBuf)); + Game::MSG_WriteString(&msg, "v"); + Game::MSG_WriteShort(&msg, clc->qport); + Game::MSG_WriteByte(&msg, vc->voicePacketCount); + + for (auto voicePacket = 0; voicePacket < vc->voicePacketCount; ++voicePacket) + { + assert(vc->voicePackets[voicePacket].dataSize > 0); + assert(vc->voicePackets[voicePacket].dataSize < (2 << 15)); + + Game::MSG_WriteByte(&msg, vc->voicePackets[voicePacket].dataSize); + Game::MSG_WriteData(&msg, vc->voicePackets[voicePacket].data, vc->voicePackets[voicePacket].dataSize); + } + + Game::NET_OutOfBandVoiceData(clc->netchan.sock, clc->serverAddress, msg.data, msg.cursize, true); + if ((*Game::cl_showSend)->current.enabled) + { + Logger::Print(Game::CON_CHANNEL_CLIENT, "voice: {}\n", msg.cursize); + } + } + + void Voice::CL_VoicePacket_Hk(const int localClientNum, Game::msg_t* msg) + { + const auto numPackets = Game::MSG_ReadByte(msg); + if (numPackets < 0 || numPackets > MAX_SERVER_QUEUED_VOICE_PACKETS) + { + return; + } + + Game::VoicePacket_t voicePacket{}; + for (auto packet = 0; packet < numPackets; ++packet) + { + voicePacket.talker = static_cast(Game::MSG_ReadByte(msg)); + voicePacket.dataSize = Game::MSG_ReadByte(msg); + if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA) + { + Logger::Print(Game::CON_CHANNEL_CLIENT, "Invalid server voice packet of {} bytes\n", voicePacket.dataSize); + return; + } + + Game::MSG_ReadData(msg, voicePacket.data, voicePacket.dataSize); + + if (static_cast(voicePacket.talker) >= Game::MAX_CLIENTS) + { + Logger::Print(Game::CON_CHANNEL_CLIENT, "Invalid voice packet - talker was {}\n", voicePacket.talker); + return; + } + + Game::SessionData* session{}; + if (Game::Party_InParty(Game::g_lobbyData)) + { + session = Game::g_lobbyData->session; + } + else if (Game::Party_InParty(Game::g_partyData)) + { + session = Game::g_partyData->session; + } + else + { + session = Game::g_serverSession; + } + + if (!Game::CL_IsPlayerMuted(session, localClientNum, voicePacket.talker)) + { + if ((*Game::cl_voice)->current.enabled) + { + Game::Voice_IncomingVoiceData(session, voicePacket.talker, reinterpret_cast(voicePacket.data), voicePacket.dataSize); + } + } + } + } + + Voice::Voice() + { + AssertOffset(Game::clientUIActive_t, connectionState, 0x9B8); + + // Write voice packets to the server instead of other clients + Utils::Hook(0x487935, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x5AD945, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A9E06, CL_VoicePacket_Hk, HOOK_CALL).install()->quick(); + + Utils::Hook(0x4519F5, SV_SendClientMessages_Stub, HOOK_CALL).install()->quick(); + + // Recycle packet handler for 'icanthear' + Utils::Hook::Set(0x62673F, "v"); + Utils::Hook(0x626787, SV_VoicePacket, HOOK_CALL).install()->quick(); + + sv_voice = Game::Dvar_RegisterBool("sv_voice", false, Game::DVAR_NONE, "Use server side voice communications"); + } +} diff --git a/src/Components/Modules/Voice.hpp b/src/Components/Modules/Voice.hpp new file mode 100644 index 00000000..211204d4 --- /dev/null +++ b/src/Components/Modules/Voice.hpp @@ -0,0 +1,34 @@ +#pragma once + +namespace Components +{ + class Voice : public Component + { + public: + Voice(); + + static bool SV_VoiceEnabled(); + + private: + static constexpr auto MAX_VOICE_PACKET_DATA = 256; + static constexpr auto MAX_SERVER_QUEUED_VOICE_PACKETS = 40; + + static Game::VoicePacket_t voicePackets[Game::MAX_CLIENTS][MAX_SERVER_QUEUED_VOICE_PACKETS]; + static int voicePacketCount[Game::MAX_CLIENTS]; + + static const Game::dvar_t* sv_voice; + + static void SV_WriteVoiceDataToClient(int clientNum, Game::msg_t* msg); + static void SV_SendClientVoiceData(Game::client_t* client); + static void SV_SendClientMessages_Stub(Game::client_t* client, Game::msg_t* msg, unsigned char* snapshotMsgBuf); + + static bool OnSameTeam(Game::gentity_s* ent1, Game::gentity_s* ent2); + static void SV_QueueVoicePacket(int talkerNum, int clientNum, Game::VoicePacket_t* voicePacket); + static void G_BroadcastVoice(Game::gentity_s* talker, Game::VoicePacket_t* voicePacket); + static void SV_UserVoice(Game::client_t* cl, Game::msg_t* msg); + static void SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg); + + static void CL_WriteVoicePacket_Hk(int localClientNum); + static void CL_VoicePacket_Hk(int localClientNum, Game::msg_t* msg); + }; +} diff --git a/src/Game/Dvars.cpp b/src/Game/Dvars.cpp index fedf2e94..de48e04a 100644 --- a/src/Game/Dvars.cpp +++ b/src/Game/Dvars.cpp @@ -20,8 +20,13 @@ namespace Game const dvar_t** sv_mapRotationCurrent = reinterpret_cast(0x2098DF0); const dvar_t** sv_maxclients = reinterpret_cast(0x2098D90); const dvar_t** sv_cheats = reinterpret_cast(0x2098DE0); + const dvar_t** sv_voiceQuality = reinterpret_cast(0x2098DB0); + + const dvar_t** cl_showSend = reinterpret_cast(0xA1E870); + const dvar_t** cl_voice = reinterpret_cast(0xB2BB44); const dvar_t** g_cheats = reinterpret_cast(0x1A45D54); + const dvar_t** g_deadChat = reinterpret_cast(0x19BD5DC); const dvar_t** version = reinterpret_cast(0x1AD7930); } diff --git a/src/Game/Dvars.hpp b/src/Game/Dvars.hpp index 3e4141a0..2f0ef3b8 100644 --- a/src/Game/Dvars.hpp +++ b/src/Game/Dvars.hpp @@ -21,8 +21,13 @@ namespace Game extern const dvar_t** sv_mapRotationCurrent; extern const dvar_t** sv_maxclients; extern const dvar_t** sv_cheats; + extern const dvar_t** sv_voiceQuality; + + extern const dvar_t** cl_showSend; + extern const dvar_t** cl_voice; extern const dvar_t** g_cheats; + extern const dvar_t** g_deadChat; extern const dvar_t** version; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 4cebf8cb..8c854d9e 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -68,6 +68,8 @@ namespace Game CL_ConsoleFixPosition_t CL_ConsoleFixPosition = CL_ConsoleFixPosition_t(0x44A430); CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount = CL_GetLocalClientActiveCount_t(0x5BAD90); CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum = CL_ControllerIndexFromClientNum_t(0x449E30); + CL_MouseEvent_t CL_MouseEvent = CL_MouseEvent_t(0x4D7C50); + CL_IsPlayerMuted_t CL_IsPlayerMuted = CL_IsPlayerMuted_t(0x4B6250); Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x470090); Cmd_AddServerCommand_t Cmd_AddServerCommand = Cmd_AddServerCommand_t(0x4DCE00); @@ -254,6 +256,7 @@ namespace Game NET_StringToAdr_t NET_StringToAdr = NET_StringToAdr_t(0x409010); NET_OutOfBandPrint_t NET_OutOfBandPrint = NET_OutOfBandPrint_t(0x4AEF00); NET_OutOfBandData_t NET_OutOfBandData = NET_OutOfBandData_t(0x49C7E0); + NET_OutOfBandVoiceData_t NET_OutOfBandVoiceData = NET_OutOfBandVoiceData_t(0x4FCC90); Live_MPAcceptInvite_t Live_MPAcceptInvite = Live_MPAcceptInvite_t(0x420A6D); Live_GetMapIndex_t Live_GetMapIndex = Live_GetMapIndex_t(0x4F6440); @@ -273,6 +276,7 @@ namespace Game PartyHost_CountMembers_t PartyHost_CountMembers = PartyHost_CountMembers_t(0x497330); PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot = PartyHost_GetMemberAddressBySlot_t(0x44E100); PartyHost_GetMemberName_t PartyHost_GetMemberName = PartyHost_GetMemberName_t(0x44BE90); + Party_InParty_t Party_InParty = Party_InParty_t(0x4F10C0); Playlist_ParsePlaylists_t Playlist_ParsePlaylists = Playlist_ParsePlaylists_t(0x4295A0); @@ -388,6 +392,7 @@ namespace Game SV_DropClient_t SV_DropClient = SV_DropClient_t(0x4D1600); SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0); SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390); + SV_FindClientByAddress_t SV_FindClientByAddress = SV_FindClientByAddress_t(0x44F450); Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580); Sys_IsDatabaseReady_t Sys_IsDatabaseReady = Sys_IsDatabaseReady_t(0x4CA4A0); @@ -458,7 +463,6 @@ namespace Game PM_GetEffectiveStance_t PM_GetEffectiveStance = PM_GetEffectiveStance_t(0x412540); PM_UpdateLean_t PM_UpdateLean = PM_UpdateLean_t(0x43DED0); - CL_MouseEvent_t CL_MouseEvent = CL_MouseEvent_t(0x4D7C50); IN_RecenterMouse_t IN_RecenterMouse = IN_RecenterMouse_t(0x463D80); IN_MouseMove_t IN_MouseMove = IN_MouseMove_t(0x64C490); @@ -483,6 +487,7 @@ namespace Game I_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80); XNAddrToString_t XNAddrToString = XNAddrToString_t(0x452690); + Voice_IncomingVoiceData_t Voice_IncomingVoiceData = Voice_IncomingVoiceData_t(0x5001A0); XAssetHeader* DB_XAssetPool = reinterpret_cast(0x7998A8); unsigned int* g_poolSize = reinterpret_cast(0x7995E8); @@ -520,6 +525,8 @@ namespace Game PartyData* g_lobbyData = reinterpret_cast(0x1081C00); PartyData* g_partyData = reinterpret_cast(0x107E500); + SessionData* g_serverSession = reinterpret_cast(0x66B7008); + int* numIP = reinterpret_cast(0x64A1E68); netIP_t* localIP = reinterpret_cast(0x64A1E28); @@ -583,7 +590,7 @@ namespace Game field_t* g_consoleField = reinterpret_cast(0xA1B6B0); clientStatic_t* cls = reinterpret_cast(0xA7FE90); - clientUIActive_t* clientUIActives = reinterpret_cast(0xB2BB8A); + clientUIActive_t* clientUIActives = reinterpret_cast(0xB2BB88); sharedUiInfo_t* sharedUiInfo = reinterpret_cast(0x62E4B78); ScreenPlacement* scrPlaceFull = reinterpret_cast(0x10843F0); @@ -648,6 +655,10 @@ namespace Game GamerSettingState* gamerSettings = reinterpret_cast(0x107D3E8); + voiceCommunication_t* cl_voiceCommunication = reinterpret_cast(0x1079DA0); + + volatile long* sv_thread_owns_game = reinterpret_cast(0x2089DB8); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); @@ -853,6 +864,11 @@ namespace Game return hash; } + int SV_GetServerThreadOwnsGame() + { + return *sv_thread_owns_game; + } + void SV_GameDropClient(int clientNum, const char* reason) { assert((*sv_maxclients)->current.integer >= 1 && (*sv_maxclients)->current.integer <= 18); @@ -967,6 +983,28 @@ namespace Game return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); } + clientConnection_t* CL_GetLocalClientConnection(const int localClientNum) + { + assert(clientConnections); + AssertIn(localClientNum, MAX_LOCAL_CLIENTS); + + return &clientConnections[localClientNum]; + } + + connstate_t CL_GetLocalClientConnectionState(const int localClientNum) + { + AssertIn(localClientNum, STATIC_MAX_LOCAL_CLIENTS); + + return clientUIActives[localClientNum].connectionState; + } + + voiceCommunication_t* CL_GetLocalClientVoiceCommunication([[maybe_unused]] const int localClientNum) + { + AssertIn(localClientNum, STATIC_MAX_LOCAL_CLIENTS); + + return cl_voiceCommunication; + } + void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out) { unsigned int v3; // xmm1_4 diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 2f3d88a3..f736de1a 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -145,6 +145,12 @@ namespace Game typedef int(__cdecl * CL_ControllerIndexFromClientNum_t)(int localActiveClientNum); extern CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum; + typedef int(__cdecl * CL_MouseEvent_t)(int x, int y, int dx, int dy); + extern CL_MouseEvent_t CL_MouseEvent; + + typedef bool(__cdecl * CL_IsPlayerMuted_t)(SessionData* session, int localClientNum, int muteClientIndex); + extern CL_IsPlayerMuted_t CL_IsPlayerMuted; + typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); extern Cmd_AddCommand_t Cmd_AddCommand; @@ -576,7 +582,7 @@ namespace Game typedef void(__cdecl * UI_ReplaceConversions_t)(const char* sourceString, ConversionArguments* arguments, char* outputString, size_t outputStringSize); extern UI_ReplaceConversions_t UI_ReplaceConversions; - typedef void(__cdecl * MSG_Init_t)(msg_t* buf, char* data, int length); + typedef void(__cdecl * MSG_Init_t)(msg_t* buf, unsigned char* data, int length); extern MSG_Init_t MSG_Init; typedef void(__cdecl * MSG_ReadData_t)(msg_t* msg, void* data, int len); @@ -591,7 +597,7 @@ namespace Game typedef int(__cdecl * MSG_ReadBits_t)(msg_t* msg, int bits); extern MSG_ReadBits_t MSG_ReadBits; - typedef short(__cdecl * MSG_ReadShort_t)(msg_t* msg); + typedef int(__cdecl * MSG_ReadShort_t)(msg_t* msg); extern MSG_ReadShort_t MSG_ReadShort; typedef __int64(__cdecl * MSG_ReadInt64_t)(msg_t* msg); @@ -609,7 +615,7 @@ namespace Game typedef int(__cdecl * MSG_ReadBitsCompress_t)(const char *from, char *to, int size); extern MSG_ReadBitsCompress_t MSG_ReadBitsCompress; - typedef void(__cdecl * MSG_WriteByte_t)(msg_t* msg, unsigned char c); + typedef void(__cdecl * MSG_WriteByte_t)(msg_t* msg, int c); extern MSG_WriteByte_t MSG_WriteByte; typedef void(__cdecl * MSG_WriteData_t)(msg_t *buf, const void *data, int length); @@ -618,7 +624,7 @@ namespace Game typedef void(__cdecl * MSG_WriteLong_t)(msg_t *msg, int c); extern MSG_WriteLong_t MSG_WriteLong; - typedef void(__cdecl * MSG_WriteShort_t)(msg_t* msg, short s); + typedef void(__cdecl * MSG_WriteShort_t)(msg_t* msg, int s); extern MSG_WriteShort_t MSG_WriteShort; typedef void(__cdecl * MSG_WriteString_t)(msg_t* msg, const char *str); @@ -660,6 +666,9 @@ namespace Game typedef void(__cdecl * NET_OutOfBandData_t)(netsrc_t sock, netadr_t adr, const char *format, int len); extern NET_OutOfBandData_t NET_OutOfBandData; + typedef int(__cdecl * NET_OutOfBandVoiceData_t)(netsrc_t sock, netadr_t adr, unsigned char* format, int len, bool voiceData); + extern NET_OutOfBandVoiceData_t NET_OutOfBandVoiceData; + typedef void(__cdecl * Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, const int controllerIndex, bool fromGameInvite); extern Live_MPAcceptInvite_t Live_MPAcceptInvite; @@ -702,6 +711,9 @@ namespace Game typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData* party, const int clientNum); extern PartyHost_GetMemberName_t PartyHost_GetMemberName; + typedef int(__cdecl * Party_InParty_t)(PartyData* party); + extern Party_InParty_t Party_InParty; + typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data); extern Playlist_ParsePlaylists_t Playlist_ParsePlaylists; @@ -957,6 +969,9 @@ namespace Game typedef client_t*(__cdecl * SV_GetPlayerByNum_t)(); extern SV_GetPlayerByNum_t SV_GetPlayerByNum; + typedef client_t*(__cdecl * SV_FindClientByAddress_t)(netadr_t from, int qport, int remoteClientIndex); + extern SV_FindClientByAddress_t SV_FindClientByAddress; + typedef void(__cdecl * Sys_Error_t)(const char* error, ...); extern Sys_Error_t Sys_Error; @@ -1116,9 +1131,6 @@ namespace Game typedef void(__cdecl * PM_UpdateLean_t)(playerState_s* ps, float msec, usercmd_s* cmd, void(*capsuleTrace)(trace_t*, const float*, const float*, const Bounds*, int, int)); extern PM_UpdateLean_t PM_UpdateLean; - typedef int(__cdecl * CL_MouseEvent_t)(int x, int y, int dx, int dy); - extern CL_MouseEvent_t CL_MouseEvent; - typedef void(__cdecl * IN_RecenterMouse_t)(); extern IN_RecenterMouse_t IN_RecenterMouse; @@ -1164,6 +1176,9 @@ namespace Game typedef void(__cdecl * XNAddrToString_t)(const XNADDR* xnaddr, char* str); extern XNAddrToString_t XNAddrToString; + typedef int(__cdecl * Voice_IncomingVoiceData_t)(const SessionData* session, int clientNum, unsigned char* data, int size); + extern Voice_IncomingVoiceData_t Voice_IncomingVoiceData; + constexpr std::size_t STATIC_MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_CLIENTS = 18; @@ -1205,6 +1220,8 @@ namespace Game extern PartyData* g_lobbyData; extern PartyData* g_partyData; + extern SessionData* g_serverSession; + extern int* numIP; extern netIP_t* localIP; @@ -1339,6 +1356,10 @@ namespace Game extern GamerSettingState* gamerSettings; + extern voiceCommunication_t* cl_voiceCommunication; + + extern volatile long* sv_thread_owns_game; + void Sys_LockRead(FastCriticalSection* critSect); void Sys_UnlockRead(FastCriticalSection* critSect); @@ -1377,6 +1398,7 @@ namespace Game void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun); void R_SetSunFromDvars(sunflare_t* sun); + int SV_GetServerThreadOwnsGame(); void SV_GameDropClient(int clientNum, const char* reason); void SV_DropAllBots(); void SV_BotUserMove(client_t* client); @@ -1399,6 +1421,9 @@ namespace Game void Com_SetParseNegativeNumbers(int parse); int CL_GetMaxXP(); + clientConnection_t* CL_GetLocalClientConnection(int localClientNum); + connstate_t CL_GetLocalClientConnectionState(int localClientNum); + voiceCommunication_t* CL_GetLocalClientVoiceCommunication(int localClientNum); void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index ac24a1e0..1387aa13 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -5234,7 +5234,7 @@ namespace Game int stringOffsets[4139]; char stringData[131072]; int dataCount; - } gameState; + }; struct HunkUser { @@ -5708,8 +5708,8 @@ namespace Game { int overflowed; int readOnly; - char *data; - char *splitData; + unsigned char *data; + unsigned char *splitData; int maxsize; int cursize; int splitSize; @@ -8761,26 +8761,15 @@ namespace Game bool isRunning; bool cgameInitialized; bool cgameInitCalled; - bool mapPreloaded; - clientMigState_t migrationState; - MigrationPers migrationPers; - MigrationVerboseState verboseMigrationState; - int verboseMigrationData; + unsigned char __pad0[0x9AC]; int keyCatchers; bool displayHUDWithKeycatchUI; connstate_t connectionState; - bool invited; - char itemsUnlocked[256]; - bool itemsUnlockedInited; - bool itemsUnlockedLastGameDirty; - unsigned __int16 itemsUnlockedLastGame[16]; - int itemsUnlockedLastGameCount; - char* itemsUnlockedBuffer; - int itemsUnlockedLocalClientNum; - int itemsUnlockedControllerIndex; - int itemsUnlockedStatsSource; + unsigned char __pad1[0x138]; }; + static_assert(sizeof(clientUIActive_t) == 0xAF4); + enum msgLocErrType_t { LOCMSG_SAFE = 0x0, @@ -8839,6 +8828,28 @@ namespace Game CRITSECT_COUNT, }; // May be incorrect + struct ClientVoicePacket_t + { + char data[256]; + int dataSize; + }; + + struct voiceCommunication_t + { + ClientVoicePacket_t voicePackets[10]; + int voicePacketCount; + int voicePacketLastTransmit; + int packetsPerSec; + int packetsPerSecStart; + }; + + struct VoicePacket_t + { + char talker; + char data[256]; + int dataSize; + }; + #pragma endregion #ifndef IDA From cb3f1f1506247da9e7da4a30c79f09efe1df8b8b Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 13 Aug 2022 18:42:32 +0200 Subject: [PATCH 2/3] [Voice] How mute --- src/Components/Modules/Voice.cpp | 90 +++++++++++++++++++++++++------- src/Components/Modules/Voice.hpp | 10 ++++ src/Game/Functions.cpp | 1 - src/Game/Functions.hpp | 3 -- 4 files changed, 82 insertions(+), 22 deletions(-) diff --git a/src/Components/Modules/Voice.cpp b/src/Components/Modules/Voice.cpp index adf233b4..7b6d5848 100644 --- a/src/Components/Modules/Voice.cpp +++ b/src/Components/Modules/Voice.cpp @@ -5,6 +5,8 @@ namespace Components Game::VoicePacket_t Voice::voicePackets[Game::MAX_CLIENTS][MAX_SERVER_QUEUED_VOICE_PACKETS]; int Voice::voicePacketCount[Game::MAX_CLIENTS]; + bool Voice::s_playerMute[Game::MAX_CLIENTS]; + const Game::dvar_t* Voice::sv_voice; bool Voice::SV_VoiceEnabled() @@ -82,7 +84,7 @@ namespace Components return false; } - void Voice::SV_QueueVoicePacket(int talkerNum, int clientNum, Game::VoicePacket_t* voicePacket) + void Voice::SV_QueueVoicePacket(const int talkerNum, const int clientNum, Game::VoicePacket_t* voicePacket) { assert(talkerNum >= 0); assert(clientNum >= 0); @@ -163,7 +165,7 @@ namespace Components } } - void Voice::CL_WriteVoicePacket_Hk(int localClientNum) + void Voice::CL_WriteVoicePacket_Hk(const int localClientNum) { const auto connstate = Game::CL_GetLocalClientConnectionState(localClientNum); const auto clc = Game::CL_GetLocalClientConnection(localClientNum); @@ -197,6 +199,38 @@ namespace Components } } + bool Voice::CL_IsPlayerMuted_Hk([[maybe_unused]] Game::SessionData* session, [[maybe_unused]] const int localClientNum, const int muteClientIndex) + { + AssertIn(muteClientIndex, Game::MAX_CLIENTS); + return s_playerMute[muteClientIndex]; + } + + void Voice::CL_MutePlayer_Hk([[maybe_unused]] Game::SessionData* session, const int muteClientIndex) + { + AssertIn(muteClientIndex, Game::MAX_CLIENTS); + s_playerMute[muteClientIndex] = true; + } + + void Voice::Voice_UnmuteMember_Hk([[maybe_unused]] Game::SessionData* session, const int clientNum) + { + AssertIn(clientNum, Game::MAX_CLIENTS); + s_playerMute[clientNum] = false; + } + + void Voice::CL_TogglePlayerMute(const int localClientNum, const int muteClientIndex) + { + AssertIn(muteClientIndex, Game::MAX_CLIENTS); + + if (CL_IsPlayerMuted_Hk(nullptr, localClientNum, muteClientIndex)) + { + Voice_UnmuteMember_Hk(nullptr, muteClientIndex); + } + else + { + CL_MutePlayer_Hk(nullptr, muteClientIndex); + } + } + void Voice::CL_VoicePacket_Hk(const int localClientNum, Game::msg_t* msg) { const auto numPackets = Game::MSG_ReadByte(msg); @@ -224,45 +258,65 @@ namespace Components return; } - Game::SessionData* session{}; - if (Game::Party_InParty(Game::g_lobbyData)) - { - session = Game::g_lobbyData->session; - } - else if (Game::Party_InParty(Game::g_partyData)) - { - session = Game::g_partyData->session; - } - else - { - session = Game::g_serverSession; - } - - if (!Game::CL_IsPlayerMuted(session, localClientNum, voicePacket.talker)) + if (!CL_IsPlayerMuted_Hk(nullptr, localClientNum, voicePacket.talker)) { if ((*Game::cl_voice)->current.enabled) { - Game::Voice_IncomingVoiceData(session, voicePacket.talker, reinterpret_cast(voicePacket.data), voicePacket.dataSize); + Game::Voice_IncomingVoiceData(nullptr, voicePacket.talker, reinterpret_cast(voicePacket.data), voicePacket.dataSize); } } } } + void Voice::UI_Mute_player(int clientNum, const int localClientNum) + { + if (Game::cgArray->clientNum != Game::sharedUiInfo->playerClientNums[clientNum]) + { + CL_TogglePlayerMute(localClientNum, Game::sharedUiInfo->playerClientNums[clientNum]); + } + } + + __declspec(naked) void Voice::UI_Mute_Player_Stub() + { + __asm + { + push eax + call UI_Mute_player + add esp, 8 // Game already pushed localClientNum + + pop edi + pop esi + add esp, 0xC00 + ret + } + } + Voice::Voice() { AssertOffset(Game::clientUIActive_t, connectionState, 0x9B8); + std::memset(voicePackets, 0, sizeof(voicePackets)); + std::memset(voicePacketCount, 0, sizeof(voicePacketCount)); + std::memset(s_playerMute, 0, sizeof(s_playerMute)); + // Write voice packets to the server instead of other clients Utils::Hook(0x487935, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x5AD945, CL_WriteVoicePacket_Hk, HOOK_CALL).install()->quick(); Utils::Hook(0x5A9E06, CL_VoicePacket_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x4B6250, CL_IsPlayerMuted_Hk, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4519F5, SV_SendClientMessages_Stub, HOOK_CALL).install()->quick(); // Recycle packet handler for 'icanthear' Utils::Hook::Set(0x62673F, "v"); Utils::Hook(0x626787, SV_VoicePacket, HOOK_CALL).install()->quick(); + Utils::Hook(0x45F041, UI_Mute_Player_Stub, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x4C6B50, Voice_UnmuteMember_Hk, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43F460, CL_MutePlayer_Hk, HOOK_JUMP).install()->quick(); + sv_voice = Game::Dvar_RegisterBool("sv_voice", false, Game::DVAR_NONE, "Use server side voice communications"); } } diff --git a/src/Components/Modules/Voice.hpp b/src/Components/Modules/Voice.hpp index 211204d4..747ee870 100644 --- a/src/Components/Modules/Voice.hpp +++ b/src/Components/Modules/Voice.hpp @@ -16,6 +16,8 @@ namespace Components static Game::VoicePacket_t voicePackets[Game::MAX_CLIENTS][MAX_SERVER_QUEUED_VOICE_PACKETS]; static int voicePacketCount[Game::MAX_CLIENTS]; + static bool s_playerMute[Game::MAX_CLIENTS]; + static const Game::dvar_t* sv_voice; static void SV_WriteVoiceDataToClient(int clientNum, Game::msg_t* msg); @@ -28,7 +30,15 @@ namespace Components static void SV_UserVoice(Game::client_t* cl, Game::msg_t* msg); static void SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg); + static bool CL_IsPlayerMuted_Hk(Game::SessionData* session, int localClientNum, int muteClientIndex); + static void CL_MutePlayer_Hk(Game::SessionData* session, const int muteClientIndex); + static void Voice_UnmuteMember_Hk(Game::SessionData* session, int clientNum); + static void CL_TogglePlayerMute(int localClientNum, int muteClientIndex); + static void CL_WriteVoicePacket_Hk(int localClientNum); static void CL_VoicePacket_Hk(int localClientNum, Game::msg_t* msg); + + static void UI_Mute_player(int clientNum, int localClientNum); + static void UI_Mute_Player_Stub(); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 8c854d9e..bf513541 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -69,7 +69,6 @@ namespace Game CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount = CL_GetLocalClientActiveCount_t(0x5BAD90); CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum = CL_ControllerIndexFromClientNum_t(0x449E30); CL_MouseEvent_t CL_MouseEvent = CL_MouseEvent_t(0x4D7C50); - CL_IsPlayerMuted_t CL_IsPlayerMuted = CL_IsPlayerMuted_t(0x4B6250); Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x470090); Cmd_AddServerCommand_t Cmd_AddServerCommand = Cmd_AddServerCommand_t(0x4DCE00); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index f736de1a..7d4f09fa 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -148,9 +148,6 @@ namespace Game typedef int(__cdecl * CL_MouseEvent_t)(int x, int y, int dx, int dy); extern CL_MouseEvent_t CL_MouseEvent; - typedef bool(__cdecl * CL_IsPlayerMuted_t)(SessionData* session, int localClientNum, int muteClientIndex); - extern CL_IsPlayerMuted_t CL_IsPlayerMuted; - typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); extern Cmd_AddCommand_t Cmd_AddCommand; From 0a30c751593862270a571bd77ed502ff82e941e2 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 13 Aug 2022 19:10:37 +0200 Subject: [PATCH 3/3] [Voice] Pregame voice chat --- src/Components/Modules/Voice.cpp | 47 ++++++++++++++++++++++++++++++-- src/Components/Modules/Voice.hpp | 3 +- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Components/Modules/Voice.cpp b/src/Components/Modules/Voice.cpp index 7b6d5848..76f7ba8a 100644 --- a/src/Components/Modules/Voice.cpp +++ b/src/Components/Modules/Voice.cpp @@ -131,7 +131,7 @@ namespace Components assert(cl->gentity); - for (int packet = 0; packet < packetCount; ++packet) + for (auto packet = 0; packet < packetCount; ++packet) { voicePacket.dataSize = Game::MSG_ReadByte(msg); if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA) @@ -149,6 +149,44 @@ namespace Components } } + void Voice::SV_PreGameUserVoice(Game::client_t* cl, Game::msg_t* msg) + { + Game::VoicePacket_t voicePacket{}; + + if (!SV_VoiceEnabled()) + { + return; + } + + const auto talker = cl - Game::svs_clients; + + AssertIn(talker, (*Game::sv_maxclients)->current.integer); + + const auto packetCount = Game::MSG_ReadByte(msg); + for (auto packet = 0; packet < packetCount; ++packet) + { + voicePacket.dataSize = Game::MSG_ReadShort(msg); + if (voicePacket.dataSize <= 0 || voicePacket.dataSize > MAX_VOICE_PACKET_DATA) + { + Logger::Print(Game::CON_CHANNEL_SERVER, "Received invalid voice packet of size {} from {}\n", voicePacket.dataSize, cl->name); + return; + } + + assert(voicePacket.dataSize <= MAX_VOICE_PACKET_DATA); + assert(msg->data); + assert(voicePacket.data); + + Game::MSG_ReadData(msg, voicePacket.data, voicePacket.dataSize); + for (auto otherPlayer = 0; otherPlayer < (*Game::sv_maxclients)->current.integer; ++otherPlayer) + { + if (otherPlayer != talker && Game::svs_clients[otherPlayer].state >= Game::CS_CONNECTED) + { + SV_QueueVoicePacket(talker, otherPlayer, &voicePacket); + } + } + } + } + void Voice::SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg) { auto qport = Game::MSG_ReadShort(msg); @@ -158,7 +196,12 @@ namespace Components return; } - if (cl->state == Game::CS_ACTIVE) + cl->lastPacketTime = *Game::svs_time; + if (cl->state < Game::CS_ACTIVE) + { + SV_PreGameUserVoice(cl, msg); + } + else { assert(cl->gentity); SV_UserVoice(cl, msg); diff --git a/src/Components/Modules/Voice.hpp b/src/Components/Modules/Voice.hpp index 747ee870..6c4cc2b7 100644 --- a/src/Components/Modules/Voice.hpp +++ b/src/Components/Modules/Voice.hpp @@ -28,10 +28,11 @@ namespace Components static void SV_QueueVoicePacket(int talkerNum, int clientNum, Game::VoicePacket_t* voicePacket); static void G_BroadcastVoice(Game::gentity_s* talker, Game::VoicePacket_t* voicePacket); static void SV_UserVoice(Game::client_t* cl, Game::msg_t* msg); + static void SV_PreGameUserVoice(Game::client_t* cl, Game::msg_t* msg); static void SV_VoicePacket(Game::netadr_t from, Game::msg_t* msg); static bool CL_IsPlayerMuted_Hk(Game::SessionData* session, int localClientNum, int muteClientIndex); - static void CL_MutePlayer_Hk(Game::SessionData* session, const int muteClientIndex); + static void CL_MutePlayer_Hk(Game::SessionData* session, int muteClientIndex); static void Voice_UnmuteMember_Hk(Game::SessionData* session, int clientNum); static void CL_TogglePlayerMute(int localClientNum, int muteClientIndex);