diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 15e36cfb..e8634b83 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -71,7 +71,7 @@ namespace Components { "9", 33554432 }, }; - unsigned int Bots::GetClientNum(Game::client_s* cl) + unsigned int Bots::GetClientNum(const Game::client_s* cl) { unsigned int num; diff --git a/src/Components/Modules/Bots.hpp b/src/Components/Modules/Bots.hpp index ee8381aa..6b1efd3c 100644 --- a/src/Components/Modules/Bots.hpp +++ b/src/Components/Modules/Bots.hpp @@ -7,7 +7,7 @@ namespace Components public: Bots(); ~Bots(); - static unsigned int GetClientNum(Game::client_s*); + static unsigned int GetClientNum(const Game::client_s*); static bool IsValidClientNum(unsigned int); private: diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index 5c849c3b..35b0609f 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -8,17 +8,26 @@ namespace Components bool Chat::SendChat; + std::unordered_set Chat::MuteList; + const char* Chat::EvaluateSay(char* text, Game::gentity_t* player) { - SendChat = true; + Chat::SendChat = true; if (text[1] == '/') { - SendChat = false; + Chat::SendChat = false; text[1] = text[0]; ++text; } + if (Chat::MuteList.find(Game::svs_clients[player->s.number].steamID) != Chat::MuteList.end()) + { + Chat::SendChat = false; + Game::SV_GameSendServerCommand(player->s.number, 0, + Utils::String::VA("%c \"You are muted\"", 0x65)); + } + TextRenderer::StripMaterialTextIcons(text, text, strlen(text) + 1); Game::Scr_AddEntity(player); @@ -194,9 +203,94 @@ namespace Components } } + void Chat::MuteClient(const Game::client_t* client) + { + if (Chat::MuteList.find(client->steamID) == Chat::MuteList.end()) + { + Chat::MuteList.insert(client->steamID); + + Logger::Print("%s was muted\n", client->name); + Game::SV_GameSendServerCommand(Bots::GetClientNum(client), 0, + Utils::String::VA("%c \"You were muted\"", 0x65)); + return; + } + + Logger::Print("%s is already muted\n", client->name); + Game::SV_GameSendServerCommand(-1, 0, + Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name)); + } + + void Chat::UnmuteClient(const Game::client_t* client) + { + Chat::MuteList.erase(client->steamID); + + Logger::Print("%s was unmuted\n", client->name); + Game::SV_GameSendServerCommand(Bots::GetClientNum(client), 0, + Utils::String::VA("%c \"You were unmuted\"", 0x65)); + } + + void Chat::AddChatCommands() + { + Command::AddSV("muteClient", [](Command::Params* params) + { + if (!Dvar::Var("sv_running").get()) + { + Logger::Print("Server is not running.\n"); + return; + } + + const auto* cmd = params->get(0); + if (params->length() < 2) + { + Logger::Print("Usage: %s : prevent the player from using the chat\n", cmd); + return; + } + + const auto* client = Game::SV_GetPlayerByNum(); + if (client != nullptr) + { + Chat::MuteClient(client); + } + }); + + Command::AddSV("unmute", [](Command::Params* params) + { + if (!Dvar::Var("sv_running").get()) + { + Logger::Print("Server is not running.\n"); + return; + } + + const auto* cmd = params->get(0); + if (params->length() < 2) + { + Logger::Print("Usage: %s \n%s all = unmute everyone\n", cmd, cmd); + return; + } + + const auto* client = Game::SV_GetPlayerByNum(); + if (client == nullptr) + { + if (params->get(1) == "all"s) + { + Logger::Print("All players were unmuted\n"); + Chat::MuteList.clear(); + } + } + else + { + Chat::UnmuteClient(client); + } + }); + } + Chat::Chat() { - cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, INT_MAX, Game::DVAR_FLAG_SAVED, "The normalized maximum width of a chat message"); + Dvar::OnInit([] + { + cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, std::numeric_limits::max(), Game::DVAR_FLAG_SAVED, "The normalized maximum width of a chat message"); + Chat::AddChatCommands(); + }); // Intercept chat sending Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick(); @@ -206,4 +300,9 @@ namespace Components // Change logic that does word splitting with new lines for chat messages to support fonticons Utils::Hook(0x592E10, CG_AddToTeamChat_Stub, HOOK_JUMP).install()->quick(); } + + Chat::~Chat() + { + Chat::MuteList.clear(); + } } diff --git a/src/Components/Modules/Chat.hpp b/src/Components/Modules/Chat.hpp index 9cf35e9c..72a564db 100644 --- a/src/Components/Modules/Chat.hpp +++ b/src/Components/Modules/Chat.hpp @@ -7,6 +7,7 @@ namespace Components static constexpr auto FONT_ICON_CHAT_WIDTH_CALCULATION_MULTIPLIER = 2.0f; public: Chat(); + ~Chat(); private: static Game::dvar_t** cg_chatHeight; @@ -15,6 +16,8 @@ namespace Components static bool SendChat; + static std::unordered_set MuteList; + static const char* EvaluateSay(char* text, Game::gentity_t* player); static void PreSayStub(); @@ -23,5 +26,9 @@ namespace Components static void CheckChatLineEnd(const char*& inputBuffer, char*& lineBuffer, float& len, int chatHeight, float chatWidth, char*& lastSpacePos, char*& lastFontIconPos, int lastColor); static void CG_AddToTeamChat(const char* text); static void CG_AddToTeamChat_Stub(); + + static void MuteClient(const Game::client_t* client); + static void UnmuteClient(const Game::client_t* client); + static void AddChatCommands(); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 86d8b5c5..d4ed5216 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -322,6 +322,8 @@ namespace Game SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0); SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0); SV_ClientThink_t SV_ClientThink = SV_ClientThink_t(0x44ADD0); + SV_GetPlayerByName_t SV_GetPlayerByName = SV_GetPlayerByName_t(0x6242B0); + SV_GetPlayerByNum_t SV_GetPlayerByNum = SV_GetPlayerByNum_t(0x624390); Sys_Error_t Sys_Error = Sys_Error_t(0x4E0200); Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 7da47215..6988806d 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -759,7 +759,7 @@ namespace Game typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)(); extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; - typedef void(__cdecl* SV_Cmd_ArgvBuffer_t)(int arg, char* buf, int size); + typedef void(__cdecl * SV_Cmd_ArgvBuffer_t)(int arg, char* buf, int size); extern SV_Cmd_ArgvBuffer_t SV_Cmd_ArgvBuffer; typedef void(__cdecl * SV_SetConfigstring_t)(int index, const char* string); @@ -771,9 +771,15 @@ namespace Game typedef bool(__cdecl * SV_Loaded_t)(); extern SV_Loaded_t SV_Loaded; - typedef void(__cdecl* SV_ClientThink_t)(client_s*, usercmd_s*); + typedef void(__cdecl * SV_ClientThink_t)(client_s*, usercmd_s*); extern SV_ClientThink_t SV_ClientThink; + typedef client_t*(__cdecl * SV_GetPlayerByName_t)(); + extern SV_GetPlayerByName_t SV_GetPlayerByName; + + typedef client_t*(__cdecl * SV_GetPlayerByNum_t)(); + extern SV_GetPlayerByNum_t SV_GetPlayerByNum; + typedef int(__cdecl * Sys_Error_t)(int, char *, ...); extern Sys_Error_t Sys_Error;