diff --git a/.gitmodules b/.gitmodules index dd66b2bc..92d0b332 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "deps/protobuf"] path = deps/protobuf url = https://github.com/google/protobuf.git - branch = 3.17.x + branch = 3.20.x [submodule "deps/udis86"] path = deps/udis86 url = https://github.com/vmt/udis86.git diff --git a/README.md b/README.md index 1309c156..e13fbcc9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ | `--copy-pdb` | Copy debug information for binaries as well to the path given via --copy-to. | | `--force-unit-tests` | Always compile unit tests. | | `--force-exception-handler` | Install custom unhandled exception handler even for Debug builds. | -| `--force-minidump-upload` | Upload minidumps even for Debug builds. | +| `--disable-binary-check` | Do not perform integrity checks on the exe. | | `--iw4x-zones` | Zonebuilder generates iw4x zones that cannot be loaded without IW4x specific patches. | ## Command line arguments diff --git a/deps/premake/json11.lua b/deps/premake/json11.lua index 3437ba57..60ea05d7 100644 --- a/deps/premake/json11.lua +++ b/deps/premake/json11.lua @@ -15,6 +15,7 @@ end function json11.project() project "json11" language "C++" + cppdialect "C++11" files { @@ -29,4 +30,4 @@ function json11.project() kind "StaticLib" end -table.insert(dependencies, json11) \ No newline at end of file +table.insert(dependencies, json11) diff --git a/deps/protobuf b/deps/protobuf index 5500c72c..6e9e6036 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 5500c72c5b616da9f0125bcfab513987a1226e2b +Subproject commit 6e9e60367d8744e86856590d8ea0e793c61deeec diff --git a/premake5.lua b/premake5.lua index 4a5fbe51..5ca05e35 100644 --- a/premake5.lua +++ b/premake5.lua @@ -82,8 +82,8 @@ newoption { } newoption { - trigger = "force-minidump-upload", - description = "Upload minidumps even for Debug builds." + trigger = "disable-binary-check", + description = "Do not perform integrity checks on the exe." } newoption { @@ -206,7 +206,7 @@ workspace "iw4x" configurations {"Debug", "Release"} language "C++" - cppdialect "C++17" + cppdialect "C++20" architecture "x86" platforms "Win32" @@ -262,12 +262,12 @@ workspace "iw4x" if _OPTIONS["force-unit-tests"] then defines {"FORCE_UNIT_TESTS"} end - if _OPTIONS["force-minidump-upload"] then - defines {"FORCE_MINIDUMP_UPLOAD"} - end if _OPTIONS["force-exception-handler"] then defines {"FORCE_EXCEPTION_HANDLER"} end + if _OPTIONS["disable-binary-check"] then + defines {"DISABLE_BINARY_CHECK"} + end if _OPTIONS["iw4x-zones"] then defines {"GENERATE_IW4X_SPECIFIC_ZONES"} end diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index b0e71229..4f7e2c40 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -109,6 +109,8 @@ namespace Components Loader::Register(new RawMouse()); Loader::Register(new Bullet()); Loader::Register(new MapRotation()); + Loader::Register(new Ceg()); + Loader::Register(new UserInfo()); Loader::Pregame = false; } diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 77e5b0c6..119cfe01 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -140,3 +140,5 @@ namespace Components #include "Modules/RawMouse.hpp" #include "Modules/Bullet.hpp" #include "Modules/MapRotation.hpp" +#include "Modules/Ceg.hpp" +#include "Modules/UserInfo.hpp" diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 06a03471..f5df53b9 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -23,7 +23,7 @@ namespace Components return; } - if (AssetHandler::AssetInterfaces.find(iAsset->getType()) != AssetHandler::AssetInterfaces.end()) + if (AssetHandler::AssetInterfaces.contains(iAsset->getType())) { Logger::Print("Duplicate asset interface: %s\n", Game::DB_GetXAssetTypeName(iAsset->getType())); delete AssetHandler::AssetInterfaces[iAsset->getType()]; @@ -58,7 +58,7 @@ namespace Components // Allow call DB_FindXAssetHeader within the hook AssetHandler::SetBypassState(true); - if (AssetHandler::TypeCallbacks.find(type) != AssetHandler::TypeCallbacks.end()) + if (AssetHandler::TypeCallbacks.contains(type)) { header = AssetHandler::TypeCallbacks[type](type, filename); } @@ -329,17 +329,17 @@ namespace Components { void* pointer = (*Game::g_streamBlocks)[offset->getUnpackedBlock()].data + offset->getUnpackedOffset(); - if (AssetHandler::Relocations.find(pointer) != AssetHandler::Relocations.end()) + if (AssetHandler::Relocations.contains(pointer)) { pointer = AssetHandler::Relocations[pointer]; } - offset->pointer = *reinterpret_cast(pointer); + offset->pointer = *static_cast(pointer); } void AssetHandler::ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder) { - if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) + if (AssetHandler::AssetInterfaces.contains(asset.type)) { AssetHandler::AssetInterfaces[asset.type]->save(asset.header, builder); } @@ -351,7 +351,7 @@ namespace Components void AssetHandler::ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder) { - if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) + if (AssetHandler::AssetInterfaces.contains(asset.type)) { AssetHandler::AssetInterfaces[asset.type]->mark(asset.header, builder); } @@ -375,7 +375,7 @@ namespace Components return { entry->second }; } - if (AssetHandler::AssetInterfaces.find(type) != AssetHandler::AssetInterfaces.end()) + if (AssetHandler::AssetInterfaces.contains(type)) { AssetHandler::AssetInterfaces[type]->load(&header, filename, builder); diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 9ccddf71..1ac097d5 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -243,7 +243,8 @@ namespace Assets { Components::Logger::Print("No replacement found for material %s with techset %s\n", asset->info.name, asset->techniqueSet->name); std::string techName = asset->techniqueSet->name; - if (techSetCorrespondance.find(techName) != techSetCorrespondance.end()) { + if (techSetCorrespondance.contains(techName)) + { auto iw4TechSetName = techSetCorrespondance[techName]; Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data()); diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index e89c73a2..7f0952d9 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -311,10 +311,13 @@ namespace Components * Should be called when a client drops from the server * but not "between levels" (Quake-III-Arena) */ - void Bots::ClientDisconnect_Hk(int clientNum) + void Bots::ClientDisconnect_Hk(const int clientNum) { g_botai[clientNum].active = false; + // Clear the overrides for UserInfo + UserInfo::ClearClientOverrides(clientNum); + // Call original function Utils::Hook::Call(0x4AA430)(clientNum); } diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index e3ac4a5c..fdc3604e 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -173,8 +173,8 @@ namespace Components list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle)); } - std::string command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data()); - Game::SV_GameSendServerCommand(-1, 0, command.data()); + const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data()); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command); } void CardTitles::ParseCustomTitles(const char* msg) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp new file mode 100644 index 00000000..136e524e --- /dev/null +++ b/src/Components/Modules/Ceg.cpp @@ -0,0 +1,47 @@ +#include + +namespace Components +{ + Ceg::Ceg() + { + Utils::Hook::Signature signature(0x401000, 0x740000); + + // Generic killer caller. + signature.add({ + "\x56\x8B\x00\x24\x0c\x85\xF6\x7F\x0E", "xx?xxxxxx", [](char* address) + { + Utils::Hook::Set(address, 0xC3); + } + }); + + signature.process(); + + // Some more generic obfuscation (mov al, 1; retn) + Utils::Hook::Set(0x471B20, 0xC301B0); + Utils::Hook::Set(0x43A070, 0xC301B0); + Utils::Hook::Set(0x4C8B30, 0xC301B0); + Utils::Hook::Set(0x469340, 0xC301B0); + + // Other checks + Utils::Hook::Set(0x401000, 0xC301B0); + Utils::Hook::Set(0x45F8B0, 0xC301B0); + Utils::Hook::Set(0x46FAE0, 0xC301B0); + + // Removed in 159 SP binaries + Utils::Hook::Nop(0x46B173, 9); + Utils::Hook::Nop(0x43CA16, 9); + Utils::Hook::Nop(0x505426, 9); + + // Something useless that can be skipped + Utils::Hook::Nop(0x4BB671, 2); + Utils::Hook::Nop(0x40A54D, 2); + + // Random checks scattered throughout the binary + Utils::Hook::Set(0x499F90, 0xC3); + Utils::Hook::Set(0x4FC700, 0xC3); + Utils::Hook::Set(0x4C4170, 0xC3); + Utils::Hook::Set(0x49E8C0, 0xC3); + Utils::Hook::Set(0x42DB00, 0xC3); + Utils::Hook::Set(0x4F4CF0, 0xC3); + } +} diff --git a/src/Components/Modules/Ceg.hpp b/src/Components/Modules/Ceg.hpp new file mode 100644 index 00000000..de02ca7c --- /dev/null +++ b/src/Components/Modules/Ceg.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace Components +{ + class Ceg : public Component + { + public: + Ceg(); + }; +} diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index 9979fb89..561d0bec 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -2,8 +2,10 @@ namespace Components { - Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast(0x7ED398); Dvar::Var Chat::cg_chatWidth; + Dvar::Var Chat::sv_disableChat; + + Game::dvar_t** Chat::cg_chatHeight = reinterpret_cast(0x7ED398); Game::dvar_t** Chat::cg_chatTime = reinterpret_cast(0x9F5DE8); bool Chat::SendChat; @@ -23,11 +25,11 @@ namespace Components } std::unique_lock lock(Chat::AccessMutex); - if (Chat::MuteList.find(Game::svs_clients[player->s.number].steamID) != Chat::MuteList.end()) + if (Chat::MuteList.contains(Game::svs_clients[player->s.number].steamID)) { lock.unlock(); Chat::SendChat = false; - Game::SV_GameSendServerCommand(player->s.number, 0, + Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You are muted\"", 0x65)); } @@ -37,6 +39,13 @@ namespace Components lock.unlock(); } + if (Chat::sv_disableChat.get()) + { + Chat::SendChat = false; + Game::SV_GameSendServerCommand(player->s.number, Game::SV_CMD_CAN_IGNORE, + Utils::String::VA("%c \"Chat is disabled\"", 0x65)); + } + TextRenderer::StripMaterialTextIcons(text, text, strlen(text) + 1); Game::Scr_AddEntity(player); @@ -217,20 +226,20 @@ namespace Components { std::unique_lock lock(Chat::AccessMutex); - if (Chat::MuteList.find(client->steamID) == Chat::MuteList.end()) + if (!Chat::MuteList.contains(client->steamID)) { Chat::MuteList.insert(client->steamID); lock.unlock(); Logger::Print("%s was muted\n", client->name); - Game::SV_GameSendServerCommand(client->gentity->s.number, 0, + Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were muted\"", 0x65)); return; } lock.unlock(); Logger::Print("%s is already muted\n", client->name); - Game::SV_GameSendServerCommand(-1, 0, + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s is already muted\"", 0x65, client->name)); } @@ -239,7 +248,7 @@ namespace Components Chat::UnmuteInternal(client->steamID); Logger::Print("%s was unmuted\n", client->name); - Game::SV_GameSendServerCommand(client->gentity->s.number, 0, + Game::SV_GameSendServerCommand(client->gentity->s.number, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"You were unmuted\"", 0x65)); } @@ -317,7 +326,11 @@ namespace Components { Dvar::OnInit([] { - cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, std::numeric_limits::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); + Chat::cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, + std::numeric_limits::max(), Game::dvar_flag::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); + Chat::sv_disableChat = Dvar::Register("sv_disableChat", false, + Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); + Chat::AddChatCommands(); }); diff --git a/src/Components/Modules/Chat.hpp b/src/Components/Modules/Chat.hpp index 134efb37..a19469ae 100644 --- a/src/Components/Modules/Chat.hpp +++ b/src/Components/Modules/Chat.hpp @@ -9,8 +9,11 @@ namespace Components Chat(); private: - static Game::dvar_t** cg_chatHeight; static Dvar::Var cg_chatWidth; + static Dvar::Var sv_disableChat; + + // Game dvars + static Game::dvar_t** cg_chatHeight; static Game::dvar_t** cg_chatTime; static bool SendChat; diff --git a/src/Components/Modules/Clantags.cpp b/src/Components/Modules/Clantags.cpp index c23a3b62..8d127a5f 100644 --- a/src/Components/Modules/Clantags.cpp +++ b/src/Components/Modules/Clantags.cpp @@ -32,7 +32,7 @@ namespace Components } std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data()); - Game::SV_GameSendServerCommand(-1, 0, command.data()); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command.data()); } const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername) diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 7d8436da..5b429a3f 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -11,14 +11,14 @@ namespace Components if (!Dvar::Var("sv_cheats").get()) { Logger::Print("CheatsOk: cheats are disabled!\n"); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65)); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_CHEATSNOTENABLED\"", 0x65)); return false; } if (ent->health < 1) { Logger::Print("CheatsOk: entity %i must be alive to use this command!\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65)); + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_MUSTBEALIVECOMMAND\"", 0x65)); return false; } @@ -66,7 +66,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Print("Noclip toggled for entity %i\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_NOCLIP) ? "GAME_NOCLIPON" : "GAME_NOCLIPOFF")); }); @@ -80,7 +80,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Print("UFO toggled for entity %i\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, (ent->client->flags & Game::PLAYER_FLAG_UFO) ? "GAME_UFOON" : "GAME_UFOOFF")); }); @@ -94,7 +94,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Print("God toggled for entity %i\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_GODMODE) ? "GAME_GODMODE_ON" : "GAME_GODMODE_OFF")); }); @@ -108,7 +108,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Print("Demigod toggled for entity %i\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_DEMI_GODMODE) ? "GAME_DEMI_GODMODE_ON" : "GAME_DEMI_GODMODE_OFF")); }); @@ -122,7 +122,7 @@ namespace Components const auto entNum = ent->s.number; Logger::Print("Notarget toggled for entity %i\n", entNum); - Game::SV_GameSendServerCommand(entNum, 0, Utils::String::VA("%c \"%s\"", 0x65, + Game::SV_GameSendServerCommand(entNum, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 0x65, (ent->flags & Game::FL_NOTARGET) ? "GAME_NOTARGETON" : "GAME_NOTARGETOFF")); }); @@ -137,7 +137,7 @@ namespace Components if (params->size() < 4 || params->size() > 6) { - Game::SV_GameSendServerCommand(ent->s.number, 0, + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"GAME_USAGE\x15: setviewpos x y z [yaw] [pitch]\n\"", 0x65)); return; } @@ -208,7 +208,7 @@ namespace Components strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE); - Game::SV_GameSendServerCommand(ent->s.number, 1, + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); @@ -236,7 +236,7 @@ namespace Components strncpy_s(ent->client->visionName[visMode], sizeof(Game::gclient_t::visionName[0]) / sizeof(char), name, _TRUNCATE); - Game::SV_GameSendServerCommand(ent->s.number, 1, + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_RELIABLE, Utils::String::VA("%c \"%s\" %i", Game::MY_CMDS[visMode], name, duration)); }); @@ -245,6 +245,82 @@ namespace Components assert(ent != nullptr); ent->client->ps.stunTime = 1000 + Game::level->time; // 1000 is the default test stun time }); + + ClientCommand::Add("give", [](Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) + { + if (!ClientCommand::CheatsOk(ent)) + return; + + if (params->size() < 2) + { + Game::SV_GameSendServerCommand(ent->s.number, Game::SV_CMD_CAN_IGNORE, + Utils::String::VA("%c \"GAME_USAGE\x15: give weapon\n\"", 0x65)); + return; + } + + Game::level->initializing = 1; + const auto* weaponName = params->get(1); + const auto weaponIndex = Game::G_GetWeaponIndexForName(weaponName); + + if (weaponIndex == 0) + { + Game::level->initializing = 0; + return; + } + + if (Game::BG_GetWeaponDef(weaponIndex)->inventoryType == Game::weapInventoryType_t::WEAPINVENTORY_ALTMODE) + { + Game::Com_PrintError(Game::CON_CHANNEL_ERROR, + "You can't directly spawn the altfire weapon '%s'. Spawn a weapon that has this altmode instead.\n", weaponName); + Game::level->initializing = 0; + return; + } + + auto* weapEnt = Game::G_Spawn(); + Utils::VectorCopy(weapEnt->r.currentOrigin, ent->r.currentOrigin); + Game::G_GetItemClassname(static_cast(weaponIndex), weapEnt); + Game::G_SpawnItem(weapEnt, static_cast(weaponIndex)); + + weapEnt->active = 1; + const auto offHandClass = Game::BG_GetWeaponDef(weaponIndex)->offhandClass; + if (offHandClass != Game::OFFHAND_CLASS_NONE) + { + auto* client = ent->client; + if ((client->ps.weapCommon.offhandPrimary != offHandClass) && (client->ps.weapCommon.offhandSecondary != offHandClass)) + { + switch (offHandClass) + { + case Game::OFFHAND_CLASS_FRAG_GRENADE: + case Game::OFFHAND_CLASS_THROWINGKNIFE: + case Game::OFFHAND_CLASS_OTHER: + client->ps.weapCommon.offhandPrimary = offHandClass; + break; + default: + client->ps.weapCommon.offhandSecondary = offHandClass; + break; + } + } + } + + Game::Touch_Item(weapEnt, ent, 0); + weapEnt->active = 0; + + if (weapEnt->r.isInUse) + { + Game::G_FreeEntity(weapEnt); + } + + Game::level->initializing = 0; + + for (std::size_t i = 0; i < std::extent_v; ++i) + { + const auto index = ent->client->ps.weaponsEquipped[i]; + if (index != 0) + { + Game::Add_Ammo(ent, index, 0, 998, 1); + } + } + }); } void ClientCommand::AddScriptFunctions() diff --git a/src/Components/Modules/Command.cpp b/src/Components/Modules/Command.cpp index 4e5e99b8..6144d1e6 100644 --- a/src/Components/Modules/Command.cpp +++ b/src/Components/Modules/Command.cpp @@ -64,7 +64,7 @@ namespace Components { const auto command = Utils::String::ToLower(name); - if (Command::FunctionMap.find(command) == Command::FunctionMap.end()) + if (!Command::FunctionMap.contains(command)) { Command::AddRaw(name, Command::MainCallback); } @@ -87,7 +87,7 @@ namespace Components const auto command = Utils::String::ToLower(name); - if (Command::FunctionMapSV.find(command) == Command::FunctionMapSV.end()) + if (!Command::FunctionMapSV.contains(command)) { Command::AddRawSV(name, Command::MainCallbackSV); @@ -168,17 +168,4 @@ namespace Components got->second(¶ms); } } - - Command::Command() - { - AssertSize(Game::cmd_function_t, 24); - - Command::Add("openLink", [](Command::Params* params) - { - if (params->size() > 1) - { - Utils::OpenUrl(params->get(1)); - } - }); - } } diff --git a/src/Components/Modules/Command.hpp b/src/Components/Modules/Command.hpp index ce949cc2..45dd9c0f 100644 --- a/src/Components/Modules/Command.hpp +++ b/src/Components/Modules/Command.hpp @@ -5,6 +5,8 @@ namespace Components class Command : public Component { public: + static_assert(sizeof(Game::cmd_function_t) == 0x18); + class Params { public: @@ -45,7 +47,7 @@ namespace Components int nesting_; }; - Command(); + Command() = default; static Game::cmd_function_t* Allocate(); diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 0d5e153c..cdc79d45 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -96,7 +96,7 @@ namespace Components } } - Game::SV_GameSendServerCommand(-1, 0, list.data()); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, list.data()); } void Dedicated::TimeWrapStub(Game::errorParm_t code, const char* message) @@ -160,11 +160,9 @@ namespace Components Dedicated::Dedicated() { // Map rotation - Dvar::OnInit([] - { - Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, - Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); - }); + + Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, + Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) { @@ -287,12 +285,12 @@ namespace Components if (!name.empty()) { - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); Game::Com_Printf(15, "%s: %s\n", name.data(), message.data()); } else { - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); Game::Com_Printf(15, "Console: %s\n", message.data()); } }); @@ -308,12 +306,12 @@ namespace Components if (!name.empty()) { - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); Game::Com_Printf(15, "%s -> %i: %s\n", name.data(), client, message.data()); } else { - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); Game::Com_Printf(15, "Console -> %i: %s\n", client, message.data()); } }); @@ -324,7 +322,7 @@ namespace Components if (params->size() < 2) return; std::string message = params->join(1); - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); Game::Com_Printf(15, "Raw: %s\n", message.data()); }); @@ -335,7 +333,7 @@ namespace Components int client = atoi(params->get(1)); std::string message = params->join(2); - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::SV_GameSendServerCommand(client, Game::SV_CMD_CAN_IGNORE, Utils::String::VA("%c \"%s\"", 104, message.data())); Game::Com_Printf(15, "Raw -> %i: %s\n", client, message.data()); }); }); diff --git a/src/Components/Modules/Friends.cpp b/src/Components/Modules/Friends.cpp index ea15a68b..b64fd46e 100644 --- a/src/Components/Modules/Friends.cpp +++ b/src/Components/Modules/Friends.cpp @@ -190,10 +190,10 @@ namespace Components { std::lock_guard _(Friends::Mutex); - const unsigned int modId = *reinterpret_cast("IW4x") | 0x80000000; + const auto modId = *reinterpret_cast("IW4x") | 0x80000000; // Split up the list - for (auto entry : Friends::FriendsList) + for (const auto& entry : Friends::FriendsList) { Steam::FriendGameInfo info; if (Steam::Proxy::SteamFriends->GetFriendGamePlayed(entry.userId, &info) && info.m_gameID.modID == modId) diff --git a/src/Components/Modules/Gamepad.cpp b/src/Components/Modules/Gamepad.cpp index 36ebd108..0d725b38 100644 --- a/src/Components/Modules/Gamepad.cpp +++ b/src/Components/Modules/Gamepad.cpp @@ -373,7 +373,7 @@ namespace Components if (!ps->weapIndex) return false; - const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex); + const auto* weaponDef = Game::BG_GetWeaponDef(static_cast(ps->weapIndex)); return weaponDef->offhandClass != Game::OFFHAND_CLASS_NONE; } @@ -448,7 +448,7 @@ namespace Components if (!AimAssist_IsLockonActive(input->localClientNum)) return; - const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex); + const auto* weaponDef = Game::BG_GetWeaponDef(static_cast(aaGlob.ps.weapIndex)); if (weaponDef->requireLockonToFire) return; @@ -532,7 +532,7 @@ namespace Components if (!ps->weapIndex) return false; - const auto* weaponDef = Game::BG_GetWeaponDef(ps->weapIndex); + const auto* weaponDef = Game::BG_GetWeaponDef(static_cast(ps->weapIndex)); if (weaponDef->requireLockonToFire) return false; @@ -565,7 +565,7 @@ namespace Components if (!AimAssist_IsSlowdownActive(&aaGlob.ps)) return; - const auto* weaponDef = Game::BG_GetWeaponDef(aaGlob.ps.weapIndex); + const auto* weaponDef = Game::BG_GetWeaponDef(static_cast(aaGlob.ps.weapIndex)); const auto aimAssistRange = AimAssist_Lerp(weaponDef->aimAssistRange, weaponDef->aimAssistRangeAds, aaGlob.adsLerp) * aim_aimAssistRangeScale.get(); const auto screenTarget = AimAssist_GetBestTarget(&aaGlob, aimAssistRange, aaGlob.tweakables.slowdownRegionWidth, aaGlob.tweakables.slowdownRegionHeight); diff --git a/src/Components/Modules/IPCPipe.cpp b/src/Components/Modules/IPCPipe.cpp index bcd8185b..76b4de01 100644 --- a/src/Components/Modules/IPCPipe.cpp +++ b/src/Components/Modules/IPCPipe.cpp @@ -161,7 +161,7 @@ namespace Components if (bResult && cbBytes) { - if (pipe->packetCallbacks.find(pipe->packet.command) != pipe->packetCallbacks.end()) + if (pipe->packetCallbacks.contains(pipe->packet.command)) { pipe->packetCallbacks[pipe->packet.command](pipe->packet.buffer); } diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index 641f1b0f..a3c87efb 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -12,7 +12,7 @@ namespace Components std::lock_guard _(Localization::LocalizeMutex); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) + if (Localization::LocalizeMap.contains(key)) { Game::LocalizeEntry* entry = Localization::LocalizeMap[key]; @@ -52,11 +52,11 @@ namespace Components { std::lock_guard _(Localization::LocalizeMutex); - if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) + if (Localization::TempLocalizeMap.contains(key)) { entry = Localization::TempLocalizeMap[key]; } - else if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) + else if (Localization::LocalizeMap.contains(key)) { entry = Localization::LocalizeMap[key]; } @@ -80,7 +80,7 @@ namespace Components std::lock_guard _(Localization::LocalizeMutex); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); - if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) + if (Localization::TempLocalizeMap.contains(key)) { Game::LocalizeEntry* entry = Localization::TempLocalizeMap[key]; if (entry->value) allocator->free(entry->value); @@ -256,11 +256,11 @@ namespace Components Game::XAssetHeader header = { nullptr }; std::lock_guard _(Localization::LocalizeMutex); - if (Localization::TempLocalizeMap.find(filename) != Localization::TempLocalizeMap.end()) + if (Localization::TempLocalizeMap.contains(filename)) { header.localize = Localization::TempLocalizeMap[filename]; } - else if (Localization::LocalizeMap.find(filename) != Localization::LocalizeMap.end()) + else if (Localization::LocalizeMap.contains(filename)) { header.localize = Localization::LocalizeMap[filename]; } diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 34a819b2..1b82de10 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -920,7 +920,7 @@ namespace Components { std::string name = gameWorld->dpvs.smodelDrawInsts[i].model->name; - if (models.find(name) == models.end()) models[name] = 1; + if (!models.contains(name)) models[name] = 1; else models[name]++; } } diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index 374b028a..4378068a 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -109,7 +109,7 @@ namespace Components Game::XSurface* tempSurfaces = allocator.allocateArray(modelSurfs->numsurfs); char* surfaceData = reinterpret_cast(modelSurfs->surfs); - if (ModelSurfs::AllocMap.find(modelSurfs->name) != ModelSurfs::AllocMap.end()) + if (ModelSurfs::AllocMap.contains(modelSurfs->name)) { Game::CModelAllocData* allocData = ModelSurfs::AllocMap[modelSurfs->name]; @@ -210,7 +210,7 @@ namespace Components if (hasCustomSurface && !ModelSurfs::AllocMap.empty()) { - auto allocData = ModelSurfs::AllocMap.find(header.modelSurfs->name); + const auto allocData = ModelSurfs::AllocMap.find(header.modelSurfs->name); if (allocData != ModelSurfs::AllocMap.end()) { Utils::Memory::FreeAlign(allocData->second->indexBuffer); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 6be17612..3c51a7f2 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -39,7 +39,7 @@ namespace Components const char* Party::GetLobbyInfo(SteamID lobby, const std::string& key) { - if (Party::LobbyMap.find(lobby.bits) != Party::LobbyMap.end()) + if (Party::LobbyMap.contains(lobby.bits)) { Network::Address address = Party::LobbyMap[lobby.bits]; @@ -58,10 +58,7 @@ namespace Components void Party::RemoveLobby(SteamID lobby) { - if (Party::LobbyMap.find(lobby.bits) != Party::LobbyMap.end()) - { - Party::LobbyMap.erase(Party::LobbyMap.find(lobby.bits)); - } + Party::LobbyMap.erase(lobby.bits); } void Party::ConnectError(const std::string& message) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index c05eac23..0f2f3f1f 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -229,17 +229,18 @@ namespace Components } } + Game::dvar_t* QuickPatch::Dvar_RegisterConMinicon(const char* dvarName, [[maybe_unused]] bool value, unsigned __int16 flags, const char* description) + { +#ifdef _DEBUG + constexpr auto value_ = true; +#else + constexpr auto value_ = false; +#endif + return Game::Dvar_RegisterBool(dvarName, value_, flags, description); + } + QuickPatch::QuickPatch() { - // quitHard - Command::Add("quitHard", [](Command::Params*) - { - int data = false; - const Utils::Library ntdll("ntdll.dll"); - ntdll.invokePascal("RtlAdjustPrivilege", 19, true, false, &data); - ntdll.invokePascal("NtRaiseHardError", 0xC000007B, 0, nullptr, nullptr, 6, &data); - }); - // Filtering any mapents that is intended for Spec:Ops gamemode (CODO) and prevent them from spawning Utils::Hook(0x5FBD6E, QuickPatch::IsDynClassnameStub, HOOK_CALL).install()->quick(); @@ -260,6 +261,8 @@ namespace Components Utils::Hook(0x51B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick(); Utils::Hook(0x5063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4FA448, QuickPatch::Dvar_RegisterConMinicon, HOOK_CALL).install()->quick(); + // Make sure preDestroy is called when the game shuts down Scheduler::OnShutdown(Loader::PreDestroy); @@ -714,17 +717,6 @@ namespace Components // Disable cheat protection for dvars Utils::Hook::Set(0x647682, 0xEB); - - // Constantly draw the mini console - Utils::Hook::Set(0x412A45, 0xEB); - - Scheduler::OnFrame([]() - { - if (*reinterpret_cast(0x62E4BAC)) - { - Game::Con_DrawMiniConsole(0, 2, 4, (Game::CL_IsCgameInitialized() ? 1.0f : 0.4f)); - } - }, true); #else // Remove missing tag message Utils::Hook::Nop(0x4EBF1A, 5); diff --git a/src/Components/Modules/QuickPatch.hpp b/src/Components/Modules/QuickPatch.hpp index b9b03407..b5cc5d9f 100644 --- a/src/Components/Modules/QuickPatch.hpp +++ b/src/Components/Modules/QuickPatch.hpp @@ -27,5 +27,7 @@ namespace Components static void CL_KeyEvent_OnEscape(); static void CL_KeyEvent_ConsoleEscape_Stub(); + + static Game::dvar_t* Dvar_RegisterConMinicon(const char* dvarName, bool value, unsigned __int16 flags, const char* description); }; } diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index dbe79ccc..0beb6ce0 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -461,7 +461,7 @@ namespace Components void Script::GetReplacedPos(const char* pos) { - if (Script::ReplacedFunctions.find(pos) != Script::ReplacedFunctions.end()) + if (Script::ReplacedFunctions.contains(pos)) { Script::ReplacedPos = Script::ReplacedFunctions[pos]; } @@ -475,7 +475,7 @@ namespace Components return; } - if (Script::ReplacedFunctions.find(what) != Script::ReplacedFunctions.end()) + if (Script::ReplacedFunctions.contains(what)) { Logger::Print("Warning: ReplacedFunctions already contains codePosValue for a function\n"); } @@ -667,7 +667,7 @@ namespace Components return; } - Game::Scr_AddBool(static_cast(Script::ScriptStorage.count(key))); // Until C++17 + Game::Scr_AddInt(static_cast(Script::ScriptStorage.count(key))); }); Script::AddFunction("StorageClear", [] // gsc: StorageClear(); diff --git a/src/Components/Modules/ScriptExtension.cpp b/src/Components/Modules/ScriptExtension.cpp index 31b55ae1..e47bf92c 100644 --- a/src/Components/Modules/ScriptExtension.cpp +++ b/src/Components/Modules/ScriptExtension.cpp @@ -417,5 +417,8 @@ namespace Components Utils::Hook(0x41BED2, ScriptExtension::Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue Utils::Hook(0x5FBF01, ScriptExtension::Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField Utils::Hook(0x4FF413, ScriptExtension::Scr_GetEntityFieldStub, HOOK_CALL).install()->quick(); // Scr_GetObjectField + + // Fix format string in Scr_RandomFloatRange + Utils::Hook::Set(0x5F10C6, "Scr_RandomFloatRange parms: %f %f "); } } diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index 5bffcf9e..edecba4d 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -451,7 +451,7 @@ namespace Components } } - void ServerList::Insert(Network::Address address, Utils::InfoString info) + void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info) { std::lock_guard _(ServerList::RefreshContainer.mutex); diff --git a/src/Components/Modules/ServerList.hpp b/src/Components/Modules/ServerList.hpp index 51f7a062..13d99ef6 100644 --- a/src/Components/Modules/ServerList.hpp +++ b/src/Components/Modules/ServerList.hpp @@ -38,7 +38,7 @@ namespace Components static void RefreshVisibleListInternal(UIScript::Token, bool refresh = false); static void UpdateVisibleList(UIScript::Token); static void InsertRequest(Network::Address address); - static void Insert(Network::Address address, Utils::InfoString info); + static void Insert(const Network::Address& address, const Utils::InfoString& info); static ServerInfo* GetCurrentServer(); diff --git a/src/Components/Modules/StringTable.cpp b/src/Components/Modules/StringTable.cpp index 8f1d590d..e9c82836 100644 --- a/src/Components/Modules/StringTable.cpp +++ b/src/Components/Modules/StringTable.cpp @@ -64,7 +64,7 @@ namespace Components std::string filename = Utils::String::ToLower(_filename); - if (StringTable::StringTableMap.find(filename) != StringTable::StringTableMap.end()) + if (StringTable::StringTableMap.contains(filename)) { header.stringTable = StringTable::StringTableMap[filename]; } diff --git a/src/Components/Modules/StructuredData.cpp b/src/Components/Modules/StructuredData.cpp index 983ee2e7..db5a514e 100644 --- a/src/Components/Modules/StructuredData.cpp +++ b/src/Components/Modules/StructuredData.cpp @@ -285,7 +285,7 @@ namespace Components // No need to patch version 155 if (newData[i].version == 155) continue; - if (patchDefinitions.find(newData[i].version) != patchDefinitions.end()) + if (patchDefinitions.contains(newData[i].version)) { auto patchData = patchDefinitions[newData[i].version]; auto otherData = otherPatchDefinitions[newData[i].version]; diff --git a/src/Components/Modules/UIFeeder.cpp b/src/Components/Modules/UIFeeder.cpp index b0e554bf..cc07d4c8 100644 --- a/src/Components/Modules/UIFeeder.cpp +++ b/src/Components/Modules/UIFeeder.cpp @@ -12,7 +12,7 @@ namespace Components const char* UIFeeder::GetItemText() { - if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) + if (UIFeeder::Feeders.contains(UIFeeder::Current.feeder)) { return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemText(UIFeeder::Current.index, UIFeeder::Current.column); } @@ -22,7 +22,7 @@ namespace Components unsigned int UIFeeder::GetItemCount() { - if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) + if (UIFeeder::Feeders.contains(UIFeeder::Current.feeder)) { return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemCount(); } @@ -313,7 +313,7 @@ namespace Components void UIFeeder::ApplyMap(UIScript::Token) { - std::string mapname = Dvar::Var("ui_map_name").get(); + const auto mapname = Dvar::Var("ui_map_name").get(); Dvar::Var("ui_mapname").set(mapname); Utils::Hook::Call(0x503B50)(mapname.data()); // Party_SetDisplayMapName @@ -321,7 +321,7 @@ namespace Components void UIFeeder::ApplyInitialMap(UIScript::Token) { - std::string mapname = Dvar::Var("ui_mapname").get(); + const auto mapname = Dvar::Var("ui_mapname").get(); Game::UI_UpdateArenas(); Game::UI_SortArenas(); diff --git a/src/Components/Modules/UIScript.cpp b/src/Components/Modules/UIScript.cpp index cd9b713c..2a591a8f 100644 --- a/src/Components/Modules/UIScript.cpp +++ b/src/Components/Modules/UIScript.cpp @@ -55,7 +55,7 @@ namespace Components bool UIScript::RunMenuScript(const char* name, const char** args) { - if (UIScript::UIScripts.find(name) != UIScript::UIScripts.end()) + if (UIScript::UIScripts.contains(name)) { UIScript::UIScripts[name](UIScript::Token(args)); return true; diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp new file mode 100644 index 00000000..8670c486 --- /dev/null +++ b/src/Components/Modules/UserInfo.cpp @@ -0,0 +1,76 @@ +#include + +namespace Components +{ + std::unordered_map UserInfo::UserInfoOverrides; + + void UserInfo::SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize) + { + Utils::Hook::Call(0x49A160)(index, buffer, bufferSize); + + Utils::InfoString map(buffer); + + if (!UserInfoOverrides.contains(index)) + { + UserInfoOverrides[index] = {}; + } + + for (const auto& [key, val] : UserInfoOverrides[index]) + { + if (val.empty()) + { + map.remove(key); + } + else + { + map.set(key, val); + } + } + + const auto userInfo = map.build(); + strncpy_s(buffer, bufferSize, userInfo.data(), _TRUNCATE); + } + + void UserInfo::ClearClientOverrides(const int client) + { + UserInfoOverrides[client].clear(); + } + + void UserInfo::ClearAllOverrides() + { + UserInfoOverrides.clear(); + } + + void UserInfo::AddScriptMethods() + { + Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self SetName() + { + const auto* ent = Game::GetPlayerEntity(entref); + const auto* name = Game::Scr_GetString(0); + + UserInfoOverrides[ent->s.number]["name"] = name; + Game::ClientUserinfoChanged(ent->s.number); + }); + + Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self ResetName() + { + const auto* ent = Game::GetPlayerEntity(entref); + + UserInfoOverrides[ent->s.number].erase("name"); + Game::ClientUserinfoChanged(ent->s.number); + }); + } + + UserInfo::UserInfo() + { + Utils::Hook(0x445268, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x478B04, SV_GetUserInfo_Stub, HOOK_CALL).install()->quick(); + + AddScriptMethods(); + + Script::OnVMShutdown([] + { + ClearAllOverrides(); + }); + } +} diff --git a/src/Components/Modules/UserInfo.hpp b/src/Components/Modules/UserInfo.hpp new file mode 100644 index 00000000..78b8e1c7 --- /dev/null +++ b/src/Components/Modules/UserInfo.hpp @@ -0,0 +1,21 @@ +#pragma once + +namespace Components +{ + class UserInfo : public Component + { + public: + UserInfo(); + + static void ClearClientOverrides(int client); + static void ClearAllOverrides(); + + private: + using userInfoMap = std::unordered_map; + static std::unordered_map UserInfoOverrides; + + static void SV_GetUserInfo_Stub(int index, char* buffer, int bufferSize); + + static void AddScriptMethods(); + }; +} diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 1715ebe7..9e060357 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -530,7 +530,7 @@ namespace Components // Check if the given pointer has already been mapped bool ZoneBuilder::Zone::hasPointer(const void* pointer) { - return (this->pointerMap.find(pointer) != this->pointerMap.end()); + return this->pointerMap.contains(pointer); } // Get stored offset for given file pointer @@ -644,7 +644,7 @@ namespace Components { if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) { - if (this->renameMap[type].find(asset) != this->renameMap[type].end()) + if (this->renameMap[type].contains(asset)) { return this->renameMap[type][asset]; } diff --git a/src/Main.cpp b/src/DllMain.cpp similarity index 96% rename from src/Main.cpp rename to src/DllMain.cpp index db9c14ad..53cf58b5 100644 --- a/src/Main.cpp +++ b/src/DllMain.cpp @@ -39,7 +39,7 @@ namespace Main popad push 6BAA2Fh // Continue init routine - push 6CA062h // ___security_init_cookie + push 6CA062h // __security_init_cookie retn } } @@ -54,6 +54,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l Steam::Proxy::RunMod(); +#ifndef DISABLE_BINARY_CHECK // Ensure we're working with our desired binary auto* _module = reinterpret_cast(0x400000); auto hash1 = Utils::Cryptography::JenkinsOneAtATime::Compute(_module + 0x1000, 0x2D531F); // .text @@ -69,6 +70,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*l DWORD oldProtect; VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment +#endif // Install entry point hook Utils::Hook(0x6BAC0F, Main::EntryPoint, HOOK_JUMP).install()->quick(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index eec32beb..c662fa35 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -68,6 +68,7 @@ namespace Game Com_Error_t Com_Error = Com_Error_t(0x4B22D0); Com_Printf_t Com_Printf = Com_Printf_t(0x402500); + Com_PrintError_t Com_PrintError = Com_PrintError_t(0x4F8C70); Com_PrintMessage_t Com_PrintMessage = Com_PrintMessage_t(0x4AA830); Com_EndParseSession_t Com_EndParseSession = Com_EndParseSession_t(0x4B80B0); Com_BeginParseSession_t Com_BeginParseSession = Com_BeginParseSession_t(0x4AAB80); @@ -156,6 +157,10 @@ namespace Game G_LogPrintf_t G_LogPrintf = G_LogPrintf_t(0x4B0150); G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540); G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840); + G_Spawn_t G_Spawn = G_Spawn_t(0x4226F0); + G_FreeEntity_t G_FreeEntity = G_FreeEntity_t(0x44C9D0); + G_SpawnItem_t G_SpawnItem = G_SpawnItem_t(0x403770); + G_GetItemClassname_t G_GetItemClassname = G_GetItemClassname_t(0x492630); G_PrintEntities_t G_PrintEntities = G_PrintEntities_t(0x4E6A50); G_GetEntityTypeName_t G_GetEntityTypeName = G_GetEntityTypeName_t(0x4EB810); @@ -431,6 +436,12 @@ namespace Game IN_Init_t IN_Init = IN_Init_t(0x45D620); IN_Shutdown_t IN_Shutdown = IN_Shutdown_t(0x426360); + Touch_Item_t Touch_Item = Touch_Item_t(0x44FA20); + + Add_Ammo_t Add_Ammo = Add_Ammo_t(0x4E1480); + + ClientUserinfoChanged_t ClientUserinfoChanged = ClientUserinfoChanged_t(0x445240); + XAssetHeader* DB_XAssetPool = reinterpret_cast(0x7998A8); unsigned int* g_poolSize = reinterpret_cast(0x7995E8); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 583077e5..bbd58284 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -40,7 +40,7 @@ namespace Game typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename); extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj; - typedef WeaponDef* (__cdecl * BG_GetWeaponDef_t)(int weaponIndex); + typedef WeaponDef*(__cdecl * BG_GetWeaponDef_t)(unsigned int weaponIndex); extern BG_GetWeaponDef_t BG_GetWeaponDef; typedef const char*(__cdecl * BG_GetEntityTypeName_t)(const int eType); @@ -139,6 +139,9 @@ namespace Game typedef void(__cdecl * Com_Printf_t)(int channel, const char *fmt, ...); extern Com_Printf_t Com_Printf; + typedef void(__cdecl * Com_PrintError_t)(int channel, const char* fmt, ...); + extern Com_PrintError_t Com_PrintError; + typedef void(__cdecl * Com_PrintMessage_t)(int channel, const char *msg, int error); extern Com_PrintMessage_t Com_PrintMessage; @@ -389,6 +392,18 @@ namespace Game typedef void(__cdecl * G_SpawnEntitiesFromString_t)(); extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString; + typedef gentity_s*(__cdecl * G_Spawn_t)(); + extern G_Spawn_t G_Spawn; + + typedef void(__cdecl * G_FreeEntity_t)(gentity_s* ed); + extern G_FreeEntity_t G_FreeEntity; + + typedef void(__cdecl * G_SpawnItem_t)(gentity_s* ent, int item); + extern G_SpawnItem_t G_SpawnItem; + + typedef void(__cdecl * G_GetItemClassname_t)(int item, gentity_s* ent); + extern G_GetItemClassname_t G_GetItemClassname; + typedef void(__cdecl * G_PrintEntities_t)(); extern G_PrintEntities_t G_PrintEntities; @@ -834,7 +849,7 @@ namespace Game typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID); extern SV_GameClientNum_Score_t SV_GameClientNum_Score; - typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, /*svscmd_type*/int type, const char* text); + typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, svscmd_type type, const char* text); extern SV_GameSendServerCommand_t SV_GameSendServerCommand; typedef void(__cdecl * SV_Cmd_TokenizeString_t)(const char* string); @@ -1029,6 +1044,15 @@ namespace Game typedef void(__cdecl * IN_Shutdown_t)(); extern IN_Shutdown_t IN_Shutdown; + typedef void(__cdecl * Touch_Item_t)(gentity_s* ent, gentity_s* other, int touched); + extern Touch_Item_t Touch_Item; + + typedef void(__cdecl * Add_Ammo_t)(gentity_s* ent, unsigned int weaponIndex, unsigned char weaponModel, int count, int fillClip); + extern Add_Ammo_t Add_Ammo; + + typedef void(__cdecl * ClientUserinfoChanged_t)(int clientNum); + extern ClientUserinfoChanged_t ClientUserinfoChanged; + extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index c2e06727..59fad1ad 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -343,6 +343,12 @@ namespace Game HITLOC_NUM } hitLocation_t; + enum svscmd_type + { + SV_CMD_CAN_IGNORE = 0x0, + SV_CMD_RELIABLE = 0x1, + }; + struct FxEffectDef; struct pathnode_t; struct pathnode_tree_t; @@ -1621,7 +1627,6 @@ namespace Game int groundEntityNum; int loopSound; int surfType; - union { int brushModel; @@ -1630,7 +1635,6 @@ namespace Game int xmodel; int primaryLight; } index; - int clientNum; int iHeadIcon; int iHeadIconTeam; @@ -1649,7 +1653,16 @@ namespace Game int fxId; int helicopterStage; } un1; - int un2; + union + { + int hintType; + struct + { + unsigned __int16 vehicleXModel; + char weaponModel; + } __s1; + int actorFlags; + } un2; clientLinkInfo_t clientLinkInfo; unsigned int partBits[6]; int clientMask[1]; diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index f3a7e72f..4ac7e709 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -36,7 +36,7 @@ namespace Steam std::pair Interface::getMethod(const std::string& method) { - if(this->methodCache.find(method) != this->methodCache.end()) + if(this->methodCache.contains(method)) { return this->methodCache[method]; } diff --git a/src/Steam/Steam.cpp b/src/Steam/Steam.cpp index 23501def..a03cec07 100644 --- a/src/Steam/Steam.cpp +++ b/src/Steam/Steam.cpp @@ -54,7 +54,7 @@ namespace Steam for (auto result : results) { - if (Callbacks::ResultHandlers.find(result.call) != Callbacks::ResultHandlers.end()) + if (Callbacks::ResultHandlers.contains(result.call)) { Callbacks::ResultHandlers[result.call]->Run(result.data, false, result.call); } diff --git a/src/Utils/Entities.cpp b/src/Utils/Entities.cpp index 04e24761..f2a38b86 100644 --- a/src/Utils/Entities.cpp +++ b/src/Utils/Entities.cpp @@ -70,7 +70,7 @@ namespace Utils { for (auto& entity : this->entities) { - if (entity.find("classname") != entity.end()) + if (entity.contains("classname")) { if (entity["classname"] == "misc_turret"s) { diff --git a/src/Utils/Hooking.cpp b/src/Utils/Hooking.cpp index c814daa1..e996e4f7 100644 --- a/src/Utils/Hooking.cpp +++ b/src/Utils/Hooking.cpp @@ -39,7 +39,7 @@ namespace Utils } } - void Hook::Signature::add(Hook::Signature::Container& container) + void Hook::Signature::add(const Hook::Signature::Container& container) { Hook::Signature::signatures.push_back(container); } diff --git a/src/Utils/Hooking.hpp b/src/Utils/Hooking.hpp index d40e25f1..7fd4143c 100644 --- a/src/Utils/Hooking.hpp +++ b/src/Utils/Hooking.hpp @@ -23,7 +23,7 @@ namespace Utils Signature() : Signature(0x400000, 0x800000) {} void process(); - void add(Container& container); + void add(const Container& container); private: void* start; diff --git a/src/Utils/InfoString.cpp b/src/Utils/InfoString.cpp index 8faa26fa..98a81bc6 100644 --- a/src/Utils/InfoString.cpp +++ b/src/Utils/InfoString.cpp @@ -2,12 +2,22 @@ namespace Utils { + InfoString::InfoString(const std::string& buffer) + { + this->parse(buffer); + } + void InfoString::set(const std::string& key, const std::string& value) { this->keyValuePairs[key] = value; } - std::string InfoString::get(const std::string& key) + void InfoString::remove(const std::string& key) + { + this->keyValuePairs.erase(key); + } + + std::string InfoString::get(const std::string& key) const { const auto value = this->keyValuePairs.find(key); if (value != this->keyValuePairs.end()) @@ -15,7 +25,7 @@ namespace Utils return value->second; } - return ""; + return {}; } void InfoString::parse(std::string buffer) @@ -25,17 +35,17 @@ namespace Utils buffer = buffer.substr(1); } - auto KeyValues = Utils::String::Split(buffer, '\\'); + const auto keyValues = Utils::String::Split(buffer, '\\'); - for (size_t i = 0; !KeyValues.empty() && i < (KeyValues.size() - 1); i += 2) + for (std::size_t i = 0; !keyValues.empty() && i < (keyValues.size() - 1); i += 2) { - const auto& key = KeyValues[i]; - const auto& value = KeyValues[i + 1]; + const auto& key = keyValues[i]; + const auto& value = keyValues[i + 1]; this->keyValuePairs[key] = value; } } - std::string InfoString::build() + std::string InfoString::build() const { std::string infoString; @@ -54,16 +64,18 @@ namespace Utils return infoString; } +#ifdef _DEBUG void InfoString::dump() { for (const auto& [key, value] : this->keyValuePairs) { - OutputDebugStringA(Utils::String::VA("%s: %s\n", key.data(), value.data())); + OutputDebugStringA(String::VA("%s: %s\n", key.data(), value.data())); } } +#endif - json11::Json InfoString::to_json() + json11::Json InfoString::to_json() const { - return this->keyValuePairs; + return {this->keyValuePairs}; } } diff --git a/src/Utils/InfoString.hpp b/src/Utils/InfoString.hpp index f63c6714..11f48599 100644 --- a/src/Utils/InfoString.hpp +++ b/src/Utils/InfoString.hpp @@ -5,19 +5,23 @@ namespace Utils class InfoString { public: - InfoString() {}; - InfoString(const std::string& buffer) : InfoString() { this->parse(buffer); }; + InfoString() = default; + explicit InfoString(const std::string& buffer); void set(const std::string& key, const std::string& value); - std::string get(const std::string& key); - std::string build(); + void remove(const std::string& key); + [[nodiscard]] std::string get(const std::string& key) const; + [[nodiscard]] std::string build() const; + +#ifdef _DEBUG void dump(); +#endif - json11::Json to_json(); + [[nodiscard]] json11::Json to_json() const; private: - std::map keyValuePairs; + std::unordered_map keyValuePairs; void parse(std::string buffer); }; } diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index 88bb7007..fde99eb7 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -108,7 +108,7 @@ namespace Utils bool isPointerMapped(void* ptr) { - return this->ptrMap.find(ptr) != this->ptrMap.end(); + return this->ptrMap.contains(ptr); } template T* getPointer(void* oldPtr) diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index c33bbda3..d90c5492 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -127,4 +127,7 @@ namespace Utils mutable std::recursive_mutex mutex; std::vector> slots; }; + + template + constexpr auto VectorCopy(T a, T b) { b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; } } diff --git a/tools/protoc.exe b/tools/protoc.exe index 87bf60b6..cc258a52 100644 Binary files a/tools/protoc.exe and b/tools/protoc.exe differ