diff --git a/dlls/CMakeLists.txt b/dlls/CMakeLists.txt index b2b6fc77..1109ea14 100644 --- a/dlls/CMakeLists.txt +++ b/dlls/CMakeLists.txt @@ -478,6 +478,7 @@ set(HOOKS_SRC hooks/hlds_hooks.cpp hooks/PluginManager.cpp hooks/rehlds.cpp + hooks/rehlds_hooks.cpp ../common/rehlds_interface.cpp ) diff --git a/dlls/game.cpp b/dlls/game.cpp index 6ed3209c..c330f308 100644 --- a/dlls/game.cpp +++ b/dlls/game.cpp @@ -85,6 +85,7 @@ cvar_t* g_psv_allow_autoaim = NULL; cvar_t *g_footsteps = NULL; cvar_t *g_developer = NULL; cvar_t *sv_max_client_edicts = NULL; +cvar_t *sv_voiceenable = NULL; cvar_t *sv_stepsize = NULL; cvar_t *sv_lowercase = NULL; @@ -280,6 +281,7 @@ void GameDLLInit( void ) g_footsteps = CVAR_GET_POINTER( "mp_footsteps" ); g_developer = CVAR_GET_POINTER( "developer" ); sv_max_client_edicts = CVAR_GET_POINTER( "sv_max_client_edicts" ); + sv_voiceenable = CVAR_GET_POINTER( "sv_voiceenable" ); sv_stepsize = CVAR_GET_POINTER( "sv_stepsize" ); sv_lowercase = CVAR_GET_POINTER( "sv_lowercase" ); @@ -342,5 +344,6 @@ void GameDLLInit( void ) SERVER_COMMAND( "exec skill.cfg\n" ); RehldsApi_Init(); + RegisterRehldsHooks(); } diff --git a/dlls/game.h b/dlls/game.h index 2a496603..e59805c7 100644 --- a/dlls/game.h +++ b/dlls/game.h @@ -100,6 +100,7 @@ EXPORT extern cvar_t *g_psv_allow_autoaim; EXPORT extern cvar_t *g_footsteps; EXPORT extern cvar_t *g_developer; EXPORT extern cvar_t *sv_max_client_edicts; +EXPORT extern cvar_t *sv_voiceenable; EXPORT extern cvar_t *sv_stepsize; EXPORT extern cvar_t *sv_lowercase; diff --git a/dlls/hooks/PluginHooks.h b/dlls/hooks/PluginHooks.h index 0b440bd7..89815391 100644 --- a/dlls/hooks/PluginHooks.h +++ b/dlls/hooks/PluginHooks.h @@ -130,6 +130,9 @@ struct HLCOOP_PLUGIN_HOOKS { // called after client data is updated by the mod and before it is returned to the engine HOOK_RETURN_DATA (*pfnUpdateClientDataPost)(const edict_t* ent, int sendweapons, clientdata_t* cd); + + // called before voice data is sent to a player. Set mute to true to block the message to the receiver + HOOK_RETURN_DATA (*pfnSendVoiceData)(int senderidx, int receiveridx, uint8_t* data, int sz, bool& mute); }; EXPORT void RegisterPlugin(void* plugin, HLCOOP_PLUGIN_HOOKS* hooks, const char* name); diff --git a/dlls/hooks/rehlds.h b/dlls/hooks/rehlds.h index e5026227..2ad9ba58 100644 --- a/dlls/hooks/rehlds.h +++ b/dlls/hooks/rehlds.h @@ -30,9 +30,16 @@ typedef struct cache_user_s #include "rehlds/public/rehlds/rehlds_api.h" bool RehldsApi_Init(); +void RegisterRehldsHooks(); +void UnregisterRehldsHooks(); EXPORT extern IRehldsApi* g_RehldsApi; EXPORT extern const RehldsFuncs_t* g_RehldsFuncs; EXPORT extern IRehldsServerData* g_RehldsData; EXPORT extern IRehldsHookchains* g_RehldsHookchains; -EXPORT extern IRehldsServerStatic* g_RehldsSvs; \ No newline at end of file +EXPORT extern IRehldsServerStatic* g_RehldsSvs; + +// can send network messages larger than 512 bytes (SVC_VOICEDATA) +// msgMode = MSG_BROADCAST or MSG_ONE_UNRELIABLE +// entindex = 1-based player index +EXPORT void rehlds_SendBigMessage(int msgMode, int msgType, void* data, int sz, int playerindex); \ No newline at end of file diff --git a/dlls/hooks/rehlds_hooks.cpp b/dlls/hooks/rehlds_hooks.cpp new file mode 100644 index 00000000..88e93a11 --- /dev/null +++ b/dlls/hooks/rehlds_hooks.cpp @@ -0,0 +1,107 @@ +#include "rehlds.h" +#include "util.h" +#include "PluginManager.h" + +void rehlds_SendBigMessage_internal(int msgType, void* data, int sz, int playerindex) { + IGameClient* dstClient = g_RehldsSvs->GetClient(playerindex - 1); + sizebuf_t* dstDatagram = dstClient->GetDatagram(); + + if (dstDatagram->cursize + sz + 3 >= dstDatagram->maxsize) { + ALERT(at_error, "Message %s too large: %d\n", msgTypeStr(msgType), sz); + return; + } + + g_RehldsFuncs->MSG_WriteByte(dstDatagram, msgType); + g_RehldsFuncs->MSG_WriteBuf(dstDatagram, sz, data); +} + +void rehlds_SendBigMessage(int msgMode, int msgType, void* data, int sz, int playerindex) { + if (!g_RehldsFuncs) { + ALERT(at_console, "Rehlds API not initialized!\n"); + return; + } + + if (msgMode != MSG_BROADCAST) { + if (playerindex > 0 && playerindex <= gpGlobals->maxClients) + rehlds_SendBigMessage_internal(msgType, data, sz, playerindex); + return; + } + + for (int i = 1; i <= gpGlobals->maxClients; i++) { + CBasePlayer* plr = UTIL_PlayerByIndex(i); + if (plr) + rehlds_SendBigMessage_internal(msgType, data, sz, i); + } +} + +void SV_ParseVoiceData_hlcoop(IGameClient* cl) { + uint8_t chReceived[4096]; + unsigned int nDataLength = g_RehldsFuncs->MSG_ReadShort(); + + if (nDataLength > sizeof(chReceived)) { + g_RehldsFuncs->DropClient(cl, FALSE, "Invalid voice data\n"); + return; + } + + g_RehldsFuncs->MSG_ReadBuf(nDataLength, chReceived); + + if (sv_voiceenable->value == 0.0f) + return; + + int sender = cl->GetId(); + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer* plr = UTIL_PlayerByIndex(i); + + if (!plr || !g_engfuncs.pfnVoice_GetClientListening(i, sender+1)) { + continue; + } + + bool pluginWantsMute = false; + + CALL_HOOKS_VOID(pfnSendVoiceData, sender + 1, i, chReceived, nDataLength, pluginWantsMute); + + if (pluginWantsMute) { + continue; + } + + IGameClient* dstClient = g_RehldsSvs->GetClient(i - 1); + sizebuf_t* dstDatagram = dstClient->GetDatagram(); + + int nSendLength = nDataLength; + if (sender + 1 == i && !dstClient->GetLoopback()) // voice_loopback + nSendLength = 0; + + if (dstDatagram->cursize + nSendLength + 6 < dstDatagram->maxsize) { + g_RehldsFuncs->MSG_WriteByte(dstDatagram, SVC_VOICEDATA); + g_RehldsFuncs->MSG_WriteByte(dstDatagram, sender); + g_RehldsFuncs->MSG_WriteShort(dstDatagram, nSendLength); + g_RehldsFuncs->MSG_WriteBuf(dstDatagram, nSendLength, chReceived); + } + } +} + +void Rehlds_HandleNetCommand(IRehldsHook_HandleNetCommand* chain, IGameClient* cl, uint8_t opcode) { + const int clc_voicedata = 8; + + if (opcode == clc_voicedata) { + SV_ParseVoiceData_hlcoop(cl); + return; + } + + chain->callNext(cl, opcode); +} + +void RegisterRehldsHooks() { + if (!g_RehldsHookchains) { + return; + } + g_RehldsHookchains->HandleNetCommand()->registerHook(&Rehlds_HandleNetCommand, HC_PRIORITY_DEFAULT + 1); +} + +void UnregisterRehldsHooks() { + if (!g_RehldsHookchains) { + return; + } + g_RehldsHookchains->HandleNetCommand()->unregisterHook(&Rehlds_HandleNetCommand); +} \ No newline at end of file