diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 623dd9cc..014a0782 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -113,6 +113,7 @@ namespace Components Loader::Register(new UserInfo()); Loader::Register(new Events()); Loader::Register(new Voice()); + Loader::Register(new Vote()); Loader::Register(new GSC()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index b8a494a9..71e3b530 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -144,5 +144,6 @@ namespace Components #include "Modules/UserInfo.hpp" #include "Modules/Events.hpp" #include "Modules/Voice.hpp" +#include "Modules/Vote.hpp" #include "Modules/GSC/GSC.hpp" diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index b2fe70c4..7d75f277 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -38,7 +38,7 @@ namespace Components if (IsMuted(player)) { SendChat = false; - Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, + Game::SV_GameSendServerCommand(player - Game::g_entities, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You are muted\"", 0x65)); } @@ -53,7 +53,7 @@ namespace Components if (sv_disableChat.get()) { SendChat = false; - Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, + Game::SV_GameSendServerCommand(player - Game::g_entities, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Chat is disabled\"", 0x65)); } @@ -236,7 +236,7 @@ namespace Components bool Chat::IsMuted(const Game::gentity_s* ent) { - const auto clientNum = ent->s.number; + const auto clientNum = ent - Game::g_entities; const auto xuid = Game::svs_clients[clientNum].steamID; const auto result = MutedList.access([&](muteList& clients) @@ -368,7 +368,7 @@ namespace Components int Chat::ChatCallback(Game::gentity_s* self, const char* codePos, const char* message, int mode) { - const auto entityId = Game::Scr_GetEntityId(self->s.number, 0); + const auto entityId = Game::Scr_GetEntityId(self - Game::g_entities, 0); Scripting::StackIsolation _; Game::Scr_AddInt(mode); diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index bd620627..c07415e3 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -3,7 +3,7 @@ namespace Components { - std::unordered_map> ClientCommand::HandlersSV; + std::unordered_map> ClientCommand::HandlersSV; bool ClientCommand::CheatsOk(const Game::gentity_s* ent) { @@ -26,7 +26,7 @@ namespace Components return true; } - void ClientCommand::Add(const char* name, const std::function& callback) + void ClientCommand::Add(const char* name, const std::function& callback) { const auto command = Utils::String::ToLower(name); @@ -57,7 +57,7 @@ namespace Components void ClientCommand::AddCheatCommands() { - ClientCommand::Add("noclip", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("noclip", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -71,7 +71,7 @@ namespace Components (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF")); }); - ClientCommand::Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("ufo", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -85,7 +85,7 @@ namespace Components (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF")); }); - ClientCommand::Add("god", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("god", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -99,7 +99,7 @@ namespace Components (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF")); }); - ClientCommand::Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("demigod", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -113,7 +113,7 @@ namespace Components (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF")); }); - ClientCommand::Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("notarget", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -127,7 +127,7 @@ namespace Components (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); }); - ClientCommand::Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("setviewpos", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { assert(ent != nullptr); @@ -163,7 +163,7 @@ namespace Components Game::TeleportPlayer(ent, origin, angles); }); - ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (!ClientCommand::CheatsOk(ent)) return; @@ -246,28 +246,28 @@ namespace Components void ClientCommand::AddDevelopmentCommands() { - ClientCommand::Add("dropallbots", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("dropallbots", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { Game::SV_DropAllBots(); }); - ClientCommand::Add("entitylist", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("entitylist", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { Game::Svcmd_EntityList_f(); }); - ClientCommand::Add("printentities", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("printentities", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { Game::G_PrintEntities(); }); - ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("entitycount", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { Logger::Print("Entity count = {}\n", Game::level->num_entities); }); // Also known as: "vis" - ClientCommand::Add("visionsetnaked", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("visionsetnaked", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (params->size() < 2) { @@ -295,7 +295,7 @@ namespace Components Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); - ClientCommand::Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("visionsetnight", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { if (params->size() < 2) { @@ -323,7 +323,7 @@ namespace Components Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); - ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("g_testCmd", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { assert(ent != nullptr); @@ -331,7 +331,7 @@ namespace Components Logger::Debug("playerState_s.stunTime is {}", ent->client->ps.stunTime); }); - ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) { assert(ent->client != nullptr); assert(ent->client->sess.connected != Game::CON_DISCONNECTED); diff --git a/src/Components/Modules/ClientCommand.hpp b/src/Components/Modules/ClientCommand.hpp index 60019358..24de58b9 100644 --- a/src/Components/Modules/ClientCommand.hpp +++ b/src/Components/Modules/ClientCommand.hpp @@ -7,11 +7,11 @@ namespace Components public: ClientCommand(); - static void Add(const char* name, const std::function& callback); + static void Add(const char* name, const std::function& callback); static bool CheatsOk(const Game::gentity_s* ent); private: - static std::unordered_map> HandlersSV; + static std::unordered_map> HandlersSV; static void ClientCommandStub(int clientNum); static void AddCheatCommands(); diff --git a/src/Components/Modules/Command.cpp b/src/Components/Modules/Command.cpp index 0df8ec9c..fd5e2fe3 100644 --- a/src/Components/Modules/Command.cpp +++ b/src/Components/Modules/Command.cpp @@ -5,7 +5,7 @@ namespace Components std::unordered_map> Command::FunctionMap; std::unordered_map> Command::FunctionMapSV; - std::string Command::Params::join(const int index) + std::string Command::Params::join(const int index) const { std::string result; @@ -24,12 +24,12 @@ namespace Components assert(Game::cmd_args->nesting < Game::CMD_MAX_NESTING); } - int Command::ClientParams::size() + int Command::ClientParams::size() const { return Game::cmd_args->argc[this->nesting_]; } - const char* Command::ClientParams::get(const int index) + const char* Command::ClientParams::get(const int index) const { if (index >= this->size()) { @@ -45,12 +45,12 @@ namespace Components assert(Game::sv_cmd_args->nesting < Game::CMD_MAX_NESTING); } - int Command::ServerParams::size() + int Command::ServerParams::size() const { return Game::sv_cmd_args->argc[this->nesting_]; } - const char* Command::ServerParams::get(const int index) + const char* Command::ServerParams::get(const int index) const { if (index >= this->size()) { diff --git a/src/Components/Modules/Command.hpp b/src/Components/Modules/Command.hpp index d6538080..9ed9889c 100644 --- a/src/Components/Modules/Command.hpp +++ b/src/Components/Modules/Command.hpp @@ -13,9 +13,9 @@ namespace Components Params() = default; virtual ~Params() = default; - virtual int size() = 0; - virtual const char* get(int index) = 0; - virtual std::string join(int index); + [[nodiscard]] virtual int size() const = 0; + [[nodiscard]] virtual const char* get(int index) const = 0; + [[nodiscard]] virtual std::string join(int index) const; virtual const char* operator[](const int index) { @@ -28,8 +28,8 @@ namespace Components public: ClientParams(); - int size() override; - const char* get(int index) override; + [[nodiscard]] int size() const override; + [[nodiscard]] const char* get(int index) const override; private: int nesting_; @@ -40,8 +40,8 @@ namespace Components public: ServerParams(); - int size() override; - const char* get(int index) override; + [[nodiscard]] int size() const override; + [[nodiscard]] const char* get(int index) const override; private: int nesting_; diff --git a/src/Components/Modules/Vote.cpp b/src/Components/Modules/Vote.cpp new file mode 100644 index 00000000..82928d16 --- /dev/null +++ b/src/Components/Modules/Vote.cpp @@ -0,0 +1,276 @@ +#include + +using namespace Utils::String; + +namespace Components +{ + std::unordered_map Vote::VoteCommands = + { + {"map_restart", HandleMapRestart}, + {"map_rotate", HandleMapRotate}, + {"typemap", HandleTypemap}, + {"map", HandleMap}, + {"g_gametype", HandleGametype}, + }; + + void Vote::DisplayVote(const Game::gentity_s* ent) + { + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_CALLEDAVOTE\x15%s\"", 0x65, ent->client->sess.cs.name)); + Game::level->voteNo = 0; + Game::level->voteYes = 1; + Game::level->voteTime = Game::level->time + 30000; + + for (auto i = 0; i < Game::level->maxclients; ++i) + { + Game::level->clients[i].ps.eFlags &= ~Game::EF_VOTED; + } + + ent->client->ps.eFlags |= Game::EF_VOTED; + Game::SV_SetConfigstring(Game::CS_VOTE_TIME, VA("%i %i", Game::level->voteTime, *Game::sv_serverId_value)); + Game::SV_SetConfigstring(Game::CS_VOTE_STRING, Game::level->voteDisplayString); + Game::SV_SetConfigstring(Game::CS_VOTE_YES, VA("%i", Game::level->voteYes)); + Game::SV_SetConfigstring(Game::CS_VOTE_NO, VA("%i", Game::level->voteNo)); + } + + bool Vote::IsInvalidVoteString(const std::string& input) + { + static const char* separators[] = { "\n", "\r", ";" }; + + return std::ranges::any_of(separators, + [&](const std::string& str) { return input.find(str) != std::string::npos; }); + } + + bool Vote::HandleMapRestart([[maybe_unused]] const Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) + { + sprintf_s(Game::level->voteString, "fast_restart"); + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_MAPRESTART"); + return true; + } + + bool Vote::HandleMapRotate([[maybe_unused]] const Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) + { + sprintf_s(Game::level->voteString, "%s", params->get(1)); + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_NEXTMAP"); + return true; + } + + bool Vote::HandleTypemap([[maybe_unused]] const Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) + { + char arg2[0x100]{}; + char arg3[0x100]{}; + + strncpy_s(arg2, params->get(2), _TRUNCATE); + strncpy_s(arg3, params->get(3), _TRUNCATE); + + if (!Game::Scr_IsValidGameType(arg2)) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_INVALIDGAMETYPE\"", 0x65)); + return false; + } + + if (!std::strcmp(arg2, (*Game::g_gametype)->current.string)) + { + arg2[0] = '\0'; + } + + const auto* mapname = Game::Dvar_RegisterString("mapname", "", Game::DVAR_ROM | Game::DVAR_SERVERINFO, "Current map name"); + if (!std::strcmp(arg3, mapname->current.string)) + { + arg3[0] = '\0'; + } + + if (!arg2[0] && !arg3[0]) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_TYPEMAP_NOCHANGE\"", 0x65)); + return false; + } + + if (arg3[0]) + { + if (arg2[0]) + { + sprintf_s(Game::level->voteString, "g_gametype %s; map %s", arg2, arg3); + } + else + { + sprintf_s(Game::level->voteString, "map %s", arg3); + } + + if (arg2[0]) + { + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_GAMETYPE\x14%s\x15 - \x14GAME_VOTE_MAP\x15%s", Game::Scr_GetGameTypeNameForScript(arg2), arg3); + } + else + { + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_MAP\x15%s", arg3); + } + } + else + { + sprintf_s(Game::level->voteString, "g_gametype %s; map_restart", arg2); + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_GAMETYPE\x14%s", Game::Scr_GetGameTypeNameForScript(arg2)); + } + + return true; + } + + bool Vote::HandleMap([[maybe_unused]] const Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) + { + sprintf_s(Game::level->voteString, "%s %s", params->get(1), params->get(2)); + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_MAP\x15%s", params->get(2)); + return true; + } + + bool Vote::HandleGametype([[maybe_unused]] const Game::gentity_s* ent, [[maybe_unused]] const Command::ServerParams* params) + { + if (!Game::Scr_IsValidGameType(params->get(2))) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_INVALIDGAMETYPE\"", 0x65)); + return false; + } + + sprintf_s(Game::level->voteString, "%s %s; map_restart", params->get(2), params->get(3)); + sprintf_s(Game::level->voteDisplayString, "GAME_VOTE_GAMETYPE\x14%s", Game::Scr_GetGameTypeNameForScript(params->get(2))); + return true; + } + + void Vote::Scr_VoteCalled(Game::gentity_s* self, const char* command, const char* param1, const char* param2) + { + Game::Scr_AddString(param2); + Game::Scr_AddString(param1); + Game::Scr_AddString(command); + Game::Scr_Notify(self, Game::scr_const->call_vote, 3); + } + + void Vote::Scr_PlayerVote(Game::gentity_s* self, const char* option) + { + Game::Scr_AddString(option); + Game::Scr_Notify(self, Game::scr_const->vote, 1); + } + + void Vote::Cmd_CallVote_f(Game::gentity_s* ent, const Command::ServerParams* params) + { + if (!(*Game::g_allowVote)->current.enabled) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_VOTINGNOTENABLED\"", 0x65)); + return; + } + + if (Game::level->numConnectedClients < 2) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_VOTINGNOTENOUGHPLAYERS\"", 0x65)); + return; + } + + if ((*Game::g_oldVoting)->current.enabled) + { + if (Game::level->voteTime) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_VOTEALREADYINPROGRESS\"", 0x65)); + return; + } + + if (ent->client->sess.voteCount >= 3) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_MAXVOTESCALLED\"", 0x65)); + return; + } + + if (ent->client->sess.cs.team == Game::TEAM_SPECTATOR) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_NOSPECTATORCALLVOTE\"", 0x65)); + return; + } + } + + if (IsInvalidVoteString(params->get(1)) || + IsInvalidVoteString(params->get(2)) || + IsInvalidVoteString(params->get(3))) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_INVALIDVOTESTRING\"", 0x65)); + return; + } + + if (!(*Game::g_oldVoting)->current.enabled) + { + Scr_VoteCalled(ent, params->get(1), params->get(2), params->get(3)); + return; + } + + const auto got = VoteCommands.find(params->get(1)); + if (got == VoteCommands.end()) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_INVALIDVOTESTRING\"", 0x65)); + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA(CallVoteDesc, 0x65)); + return; + } + + if (Game::level->voteExecuteTime) + { + Game::level->voteExecuteTime = 0; + Game::Cbuf_AddText(0, VA("%s\n", Game::level->voteString)); + } + + const auto shouldDisplay = got->second(ent, params); + if (shouldDisplay) + { + DisplayVote(ent); + } + } + + void Vote::Cmd_Vote_f(Game::gentity_s* ent, const Command::ServerParams* params) + { + char arg1[0x100]{}; + + if ((*Game::g_oldVoting)->current.enabled) + { + if (!Game::level->voteTime) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_NOVOTEINPROGRESS\"", 0x65)); + return; + } + + if ((ent->client->ps.eFlags & Game::EF_VOTED) != 0) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_VOTEALREADYCAST\"", 0x65)); + return; + } + + if (ent->client->sess.cs.team == Game::TEAM_SPECTATOR) + { + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_NOSPECTATORVOTE\"", 0x65)); + return; + } + + Game::SV_GameSendServerCommand(ent - Game::g_entities, Game::SV_CMD_CAN_IGNORE, VA("%c \"GAME_VOTECAST\"", 0x65)); + ent->client->ps.eFlags |= Game::EF_VOTED; + } + + strncpy_s(arg1, params->get(1), _TRUNCATE); + if (arg1[0] == 'y' || arg1[0] == 'Y' || arg1[0] == '1') + { + if ((*Game::g_oldVoting)->current.enabled) + { + Game::SV_SetConfigstring(Game::CS_VOTE_YES, VA("%i", ++Game::level->voteYes)); + } + else + { + Scr_PlayerVote(ent, "yes"); + } + } + else if ((*Game::g_oldVoting)->current.enabled) + { + Game::SV_SetConfigstring(Game::CS_VOTE_NO, VA("%i", ++Game::level->voteNo)); + } + else + { + Scr_PlayerVote(ent, "no"); + } + } + + Vote::Vote() + { + ClientCommand::Add("callvote", Cmd_CallVote_f); + ClientCommand::Add("vote", Cmd_Vote_f); + } +} diff --git a/src/Components/Modules/Vote.hpp b/src/Components/Modules/Vote.hpp new file mode 100644 index 00000000..c9e241e8 --- /dev/null +++ b/src/Components/Modules/Vote.hpp @@ -0,0 +1,31 @@ +#pragma once + +namespace Components +{ + class Vote : public Component + { + public: + Vote(); + + private: + using CommandHandler = std::function; + static std::unordered_map VoteCommands; + + static constexpr auto* CallVoteDesc = "%c \"GAME_VOTECOMMANDSARE\x15 map_restart, map_rotate, map , g_gametype , typemap \""; + + static void DisplayVote(const Game::gentity_s* ent); + static bool IsInvalidVoteString(const std::string& input); + + static bool HandleMapRestart(const Game::gentity_s* ent, const Command::ServerParams* params); + static bool HandleMapRotate(const Game::gentity_s* ent, const Command::ServerParams* params); + static bool HandleTypemap(const Game::gentity_s* ent, const Command::ServerParams* params); + static bool HandleMap(const Game::gentity_s* ent, const Command::ServerParams* params); + static bool HandleGametype(const Game::gentity_s* ent, const Command::ServerParams* params); + + static void Scr_VoteCalled(Game::gentity_s* self, const char* command, const char* param1, const char* param2); + static void Scr_PlayerVote(Game::gentity_s* self, const char* option); + + static void Cmd_CallVote_f(Game::gentity_s* ent, const Command::ServerParams* params); + static void Cmd_Vote_f(Game::gentity_s* ent, const Command::ServerParams* params); + }; +} diff --git a/src/Game/Dvars.cpp b/src/Game/Dvars.cpp index de48e04a..2d8370a4 100644 --- a/src/Game/Dvars.cpp +++ b/src/Game/Dvars.cpp @@ -27,6 +27,9 @@ namespace Game const dvar_t** g_cheats = reinterpret_cast(0x1A45D54); const dvar_t** g_deadChat = reinterpret_cast(0x19BD5DC); + const dvar_t** g_allowVote = reinterpret_cast(0x19BD644); + const dvar_t** g_oldVoting = reinterpret_cast(0x1A45DEC); + const dvar_t** g_gametype = reinterpret_cast(0x1A45DC8); const dvar_t** version = reinterpret_cast(0x1AD7930); } diff --git a/src/Game/Dvars.hpp b/src/Game/Dvars.hpp index 2f0ef3b8..5433f3ed 100644 --- a/src/Game/Dvars.hpp +++ b/src/Game/Dvars.hpp @@ -28,6 +28,9 @@ namespace Game extern const dvar_t** g_cheats; extern const dvar_t** g_deadChat; + extern const dvar_t** g_allowVote; + extern const dvar_t** g_oldVoting; + extern const dvar_t** g_gametype; extern const dvar_t** version; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index c9ddab80..b4e86f25 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -292,6 +292,8 @@ namespace Game RemoveRefToObject_t RemoveRefToObject = RemoveRefToObject_t(0x437190); + Scr_GetGameTypeNameForScript_t Scr_GetGameTypeNameForScript = Scr_GetGameTypeNameForScript_t(0x462460); + Scr_IsValidGameType_t Scr_IsValidGameType = Scr_IsValidGameType_t(0x4F1B60); Scr_LoadGameType_t Scr_LoadGameType = Scr_LoadGameType_t(0x4D9520); Scr_StartupGameType_t Scr_StartupGameType = Scr_StartupGameType_t(0x438720); @@ -484,6 +486,7 @@ namespace Game Z_VirtualAlloc_t Z_VirtualAlloc = Z_VirtualAlloc_t(0x4CFBA0); I_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80); + I_CleanStr_t I_CleanStr = I_CleanStr_t(0x4AD470); XNAddrToString_t XNAddrToString = XNAddrToString_t(0x452690); @@ -505,6 +508,7 @@ namespace Game float* cgameFOVSensitivityScale = reinterpret_cast(0xB2F884); int* svs_time = reinterpret_cast(0x31D9384); + int* sv_serverId_value = reinterpret_cast(0x2089DC0); int* svs_clientCount = reinterpret_cast(0x31D938C); client_t* svs_clients = reinterpret_cast(0x31D9390); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index e8e1272d..ef94baef 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -771,6 +771,12 @@ namespace Game typedef void(__cdecl * Scr_ShutdownAllocNode_t)(); extern Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode; + typedef char*(__cdecl * Scr_GetGameTypeNameForScript_t)(const char* pszGameTypeScript); + extern Scr_GetGameTypeNameForScript_t Scr_GetGameTypeNameForScript; + + typedef int(__cdecl * Scr_IsValidGameType_t)(const char* pszGameType); + extern Scr_IsValidGameType_t Scr_IsValidGameType; + typedef void(__cdecl * Scr_LoadGameType_t)(); extern Scr_LoadGameType_t Scr_LoadGameType; @@ -1170,6 +1176,9 @@ namespace Game typedef void(__cdecl * I_strncpyz_t)(char* dest, const char* src, int destsize); extern I_strncpyz_t I_strncpyz; + typedef char*(__cdecl * I_CleanStr_t)(char* string); + extern I_CleanStr_t I_CleanStr; + typedef void(__cdecl * XNAddrToString_t)(const XNADDR* xnaddr, char* str); extern XNAddrToString_t XNAddrToString; @@ -1196,6 +1205,7 @@ namespace Game extern float* cgameFOVSensitivityScale; extern int* svs_time; + extern int* sv_serverId_value; extern int* svs_clientCount; extern client_t* svs_clients; diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 852aec3b..b0153a16 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -220,7 +220,7 @@ namespace Game DVAR_SOURCE_DEVGUI = 0x3, }; - typedef enum : char + enum dvar_type : char { DVAR_TYPE_BOOL = 0x0, DVAR_TYPE_FLOAT = 0x1, @@ -233,7 +233,7 @@ namespace Game DVAR_TYPE_COLOR = 0x8, DVAR_TYPE_FLOAT_3_COLOR = 0x9, DVAR_TYPE_COUNT = 0xA, - } dvar_type; + }; enum clientState_t { @@ -264,6 +264,17 @@ namespace Game ERR_MAPLOADERRORSUMMARY = 0x7 }; + enum ConfigString + { + CS_VOTE_TIME = 0x11, + CS_VOTE_STRING = 0x12, + CS_VOTE_YES = 0x13, + CS_VOTE_NO = 0x14, + CS_VOTE_MAPNAME = 0x15, + CS_VOTE_GAMETYPE = 0x16, + CS_ITEMS = 0x102A, + }; // Incomplete + enum conChannel_t { CON_CHANNEL_DONT_FILTER, @@ -1258,13 +1269,7 @@ namespace Game int flags; }; - struct $3EB5F037EADAEE8E2FA2A1F9FFF31312 - { - hudelem_s current[31]; - hudelem_s archival[31]; - }; - - enum playerStateFlag + enum { PMF_PRONE = 1 << 0, PMF_DUCKED = 1 << 1, @@ -1291,7 +1296,7 @@ namespace Game PMF_DIVING = 1 << 22, }; - enum playerStateOtherFlag + enum { POF_INVULNERABLE = 1 << 0, POF_REMOTE_EYES = 1 << 1, @@ -1327,7 +1332,7 @@ namespace Game PM_DEAD_LINKED = 0x9, }; - enum playerEFlag + enum { EF_NONSOLID_BMODEL = 1 << 0, EF_TELEPORT_BIT = 1 << 1, @@ -1355,14 +1360,14 @@ namespace Game EF_SOFT = 1 << 23, }; - enum playerLinkFlag + enum { PLF_ANGLES_LOCKED = 1 << 0, PLF_USES_OFFSET = 1 << 1, PLF_WEAPONVIEW_ONLY = 1 << 2, }; - enum playerWeaponFlag + enum { PWF_USE_RELOAD = 1 << 0, PWF_USING_OFFHAND = 1 << 1, @@ -1497,14 +1502,18 @@ namespace Game int killCamEntity; int killCamLookAtEntity; int killCamClientNum; - $3EB5F037EADAEE8E2FA2A1F9FFF31312 hud; + struct + { + hudelem_s current[31]; + hudelem_s archival[31]; + } hud; unsigned int partBits[6]; int recoilScale; int diveDirection; int stunTime; }; - static_assert(sizeof(Game::playerState_s) == 0x311C); + static_assert(sizeof(playerState_s) == 0x311C); enum LocSelInputState {