From f9554e7b7bb10afdfe53d4f08e898355d84064a3 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 24 Jun 2022 21:22:46 +0200 Subject: [PATCH 01/77] [MapRotation] Some backwards compatibility Ook, ook, eek --- src/Components/Modules/MapRotation.cpp | 94 ++++++++++++++++++++------ src/Components/Modules/MapRotation.hpp | 6 +- 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/Components/Modules/MapRotation.cpp b/src/Components/Modules/MapRotation.cpp index 4991d48e..906c594a 100644 --- a/src/Components/Modules/MapRotation.cpp +++ b/src/Components/Modules/MapRotation.cpp @@ -6,6 +6,7 @@ namespace Components Dvar::Var MapRotation::SVDontRotate; Game::dvar_t** MapRotation::SVMapRotation = reinterpret_cast(0x62C7C44); + Game::dvar_t** MapRotation::SVMapRotationCurrent = reinterpret_cast(0x2098DF0); Game::dvar_t** MapRotation::SVMapname = reinterpret_cast(0x2098DDC); MapRotation::RotationData MapRotation::DedicatedRotation; @@ -98,18 +99,18 @@ namespace Components return; } + loaded = true; + try { DedicatedRotation.parse(data); } catch (const std::exception& ex) { - Logger::Print(Game::CON_CHANNEL_SERVER, "{}: sv_mapRotation contains invalid data!\n", ex.what()); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotation)->name); } Logger::Debug("DedicatedRotation size after parsing is '{}'", DedicatedRotation.getEntriesSize()); - - loaded = true; } void MapRotation::AddMapRotationCommands() @@ -155,6 +156,27 @@ namespace Components return true; } + void MapRotation::ApplyMap(const std::string& map) + { + assert(!map.empty()); + + if (Dvar::Var("sv_cheats").get()) + { + Command::Execute(Utils::String::VA("devmap %s", map.data()), true); + } + else + { + Command::Execute(Utils::String::VA("map %s", map.data()), true); + } + } + + void MapRotation::ApplyGametype(const std::string& gametype) + { + assert(!gametype.empty()); + + Dvar::Var("g_gametype").set(gametype.data()); + } + void MapRotation::RestartCurrentMap() { std::string svMapname = (*SVMapname)->current.string; @@ -165,30 +187,25 @@ namespace Components svMapname = "mp_afghan"; } - if (!Dvar::Var("sv_cheats").get()) - { - Command::Execute(Utils::String::VA("map %s", svMapname.data()), true); - } - else - { - Command::Execute(Utils::String::VA("devmap %s", svMapname.data()), true); - } + ApplyMap(svMapname); } - void MapRotation::ApplyMapRotation() + void MapRotation::ApplyRotation(RotationData& rotation) { + assert(rotation.getEntriesSize() != 0); + // Continue to apply gametype until a map is found auto foundMap = false; std::size_t i = 0; - while (!foundMap && i < DedicatedRotation.getEntriesSize()) + while (!foundMap && i < rotation.getEntriesSize()) { - const auto& entry = DedicatedRotation.getNextEntry(); + const auto& entry = rotation.getNextEntry(); if (entry.first == "map") { Logger::Debug("Loading new map: '{}'", entry.second); - Command::Execute(Utils::String::VA("map %s", entry.second.data()), true); + ApplyMap(entry.second); // Map was found so we exit the loop foundMap = true; @@ -196,13 +213,43 @@ namespace Components else if (entry.first == "gametype") { Logger::Debug("Applying new gametype: '{}'", entry.second); - Dvar::Var("g_gametype").set(entry.second); + ApplyGametype(entry.second); } ++i; } } + void MapRotation::ApplyMapRotationCurrent(const std::string& data) + { + assert(!data.empty()); + + // Ook, ook, eek + Logger::Warning(Game::CON_CHANNEL_SERVER, "You are using deprecated {}", (*SVMapRotationCurrent)->name); + + RotationData currentRotation; + try + { + Logger::Debug("Parsing {}", (*SVMapRotationCurrent)->name); + currentRotation.parse(data); + } + catch (const std::exception& ex) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}: {} contains invalid data!\n", ex.what(), (*SVMapRotationCurrent)->name); + } + + Game::Dvar_SetString(*SVMapRotationCurrent, ""); + + if (currentRotation.getEntriesSize() == 0) + { + Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotationCurrent)->name); + RestartCurrentMap(); + return; + } + + ApplyRotation(currentRotation); + } + void MapRotation::RandomizeMapRotation() { if (SVRandomMapRotation.get()) @@ -224,8 +271,17 @@ namespace Components } Logger::Print(Game::CON_CHANNEL_SERVER, "Rotating map...\n"); - const std::string mapRotation = (*SVMapRotation)->current.string; + // This takes priority because of backwards compatibility + const std::string mapRotationCurrent = (*SVMapRotationCurrent)->current.string; + if (!mapRotationCurrent.empty()) + { + Logger::Debug("Applying {}", (*SVMapRotationCurrent)->name); + ApplyMapRotationCurrent(mapRotationCurrent); + return; + } + + const std::string mapRotation = (*SVMapRotation)->current.string; // People may have sv_mapRotation empty because they only use 'addMap' or 'addGametype' if (!mapRotation.empty()) { @@ -235,14 +291,14 @@ namespace Components if (DedicatedRotation.getEntriesSize() == 0) { - Logger::Print(Game::CON_CHANNEL_SERVER, "sv_mapRotation is empty or contains invalid data. Restarting map\n"); + Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotation)->name); RestartCurrentMap(); return; } RandomizeMapRotation(); - ApplyMapRotation(); + ApplyRotation(DedicatedRotation); } MapRotation::MapRotation() diff --git a/src/Components/Modules/MapRotation.hpp b/src/Components/Modules/MapRotation.hpp index 7cee6255..3c4a0efe 100644 --- a/src/Components/Modules/MapRotation.hpp +++ b/src/Components/Modules/MapRotation.hpp @@ -47,6 +47,7 @@ namespace Components static Dvar::Var SVDontRotate; // Game Dvars static Game::dvar_t** SVMapRotation; + static Game::dvar_t** SVMapRotationCurrent; static Game::dvar_t** SVMapname; // Holds the parsed data from sv_mapRotation @@ -58,8 +59,11 @@ namespace Components static void AddMapRotationCommands(); static bool ShouldRotate(); + static void ApplyMap(const std::string& map); + static void ApplyGametype(const std::string& gametype); static void RestartCurrentMap(); - static void ApplyMapRotation(); + static void ApplyRotation(RotationData& rotation); + static void ApplyMapRotationCurrent(const std::string& data); static void RandomizeMapRotation(); static void SV_MapRotate_f(); From 2cf741f2a2b0447f5078f9882c67cf132d35e6c5 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 24 Jun 2022 21:33:34 +0200 Subject: [PATCH 02/77] [General] Update changelog --- CHANGELOG.md | 1 + src/Components/Modules/MapRotation.cpp | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 895266dc..1c1a49aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. ### Changed - `openLink` command was removed for security reasons (#286) +- `sv_mapRotationCurrent` is not being used anymore (#302) ### Fixed diff --git a/src/Components/Modules/MapRotation.cpp b/src/Components/Modules/MapRotation.cpp index 906c594a..30adf749 100644 --- a/src/Components/Modules/MapRotation.cpp +++ b/src/Components/Modules/MapRotation.cpp @@ -173,7 +173,6 @@ namespace Components void MapRotation::ApplyGametype(const std::string& gametype) { assert(!gametype.empty()); - Dvar::Var("g_gametype").set(gametype.data()); } From 6cf7defdec2be4e777d7312fcc2034b08470949c Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 25 Jun 2022 14:22:13 +0200 Subject: [PATCH 03/77] [Script] Fix bug --- src/Components/Modules/Script.cpp | 96 ++++++++++++++++--------------- src/Components/Modules/Script.hpp | 14 +++-- src/Game/Functions.cpp | 1 + src/Game/Functions.hpp | 9 ++- 4 files changed, 66 insertions(+), 54 deletions(-) diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 95d64240..1d9bd018 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -3,16 +3,19 @@ namespace Components { std::string Script::ScriptName; - std::vector Script::ScriptHandles; std::unordered_map Script::CustomScrFunctions; std::unordered_map Script::CustomScrMethods; std::vector Script::ScriptNameStack; unsigned short Script::FunctionName; std::unordered_map Script::ScriptStorage; std::unordered_map Script::ScriptBaseProgramNum; + int Script::LastFrameTime = -1; + std::unordered_map Script::ReplacedFunctions; const char* Script::ReplacedPos = nullptr; - int Script::LastFrameTime = -1; + + std::vector Script::ScriptMainHandles; + std::vector Script::ScriptInitHandles; void Script::FunctionError() { @@ -201,68 +204,70 @@ namespace Components Logger::Error(Game::ERR_SCRIPT_DROP, "script compile error\n{}\n{}\n(see console for actual details)\n", msgbuf, Script::ScriptName); } - int Script::LoadScriptAndLabel(const std::string& script, const std::string& label) + void Script::Scr_LoadGameType_Stub() { - Logger::Print("Loading script {}.gsc...\n", script); - - if (!Game::Scr_LoadScript(script.data())) + for (const auto& handle : Script::ScriptMainHandles) { - Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script); - Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script); - } - else - { - Logger::Print("Script {}.gsc loaded successfully.\n", script); - } - - Logger::Debug("Finding script handle {}::{}...", script.data(), label.data()); - const auto handle = Game::Scr_GetFunctionHandle(script.data(), label.data()); - if (handle) - { - Logger::Print("Script handle {}::{} loaded successfully.\n", script, label); - return handle; - } - - Logger::Print("Script handle {}::{} couldn't be loaded. (file with no entry point?)\n", script, label); - return handle; - } - - void Script::LoadGameType() - { - for (const auto& handle : Script::ScriptHandles) - { - Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0)); + const auto id = Game::Scr_ExecThread(handle, 0); + Game::Scr_FreeThread(static_cast(id)); } Game::Scr_LoadGameType(); } - void Script::LoadGameTypeScript() + void Script::Scr_StartupGameType_Stub() { - Script::ScriptHandles.clear(); + for (const auto& handle : Script::ScriptInitHandles) + { + const auto id = Game::Scr_ExecThread(handle, 0); + Game::Scr_FreeThread(static_cast(id)); + } + + Game::Scr_StartupGameType(); + } + + void Script::GScr_LoadGameTypeScript_Stub() + { + // Clear handles (from previous GSC loading session) + Script::ScriptMainHandles.clear(); + Script::ScriptInitHandles.clear(); const auto list = FileSystem::GetFileList("scripts/", "gsc"); - for (auto file : list) + for (const auto& file : list) { - file.insert(0, "scripts/"); + std::string script = "scripts/" + file; - if (Utils::String::EndsWith(file, ".gsc")) + if (Utils::String::EndsWith(script, ".gsc")) { - file = file.substr(0, file.size() - 4); + script = script.substr(0, script.size() - 4); } - auto handle = Script::LoadScriptAndLabel(file, "init"); + Logger::Print("Loading script {}.gsc...\n", script); - if (handle) + if (!Game::Scr_LoadScript(script.data())) { - Script::ScriptHandles.push_back(handle); + Logger::Print("Script {} encountered an error while loading. (doesn't exist?)", script); + Logger::Error(Game::ERR_DROP, "Could not find script '{}'", script); + return; } - else + + Logger::Print("Script {}.gsc loaded successfully.\n", script); + Logger::Debug("Finding script handle main or init..."); + + const auto initHandle = Game::Scr_GetFunctionHandle(script.data(), "init"); + if (initHandle != 0) { - handle = Script::LoadScriptAndLabel(file, "main"); - if (handle) Script::ScriptHandles.push_back(handle); + Script::ScriptInitHandles.push_back(initHandle); } + + const auto mainHandle = Game::Scr_GetFunctionHandle(script.data(), "main"); + if (mainHandle != 0) + { + Script::ScriptMainHandles.push_back(mainHandle); + } + + // Allow scripts with no handles } Game::GScr_LoadGameTypeScript(); @@ -687,8 +692,9 @@ namespace Components Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick(); - Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick(); - Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick(); + Utils::Hook(0x48EFFE, Script::Scr_LoadGameType_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x48F008, Script::Scr_StartupGameType_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x45D44A, Script::GScr_LoadGameTypeScript_Stub, HOOK_CALL).install()->quick(); // Fetch custom functions Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index 8ef576ab..73cbfeab 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -8,8 +8,6 @@ namespace Components public: Script(); - static int LoadScriptAndLabel(const std::string& script, const std::string& label); - static void AddFunction(const char* name, Game::BuiltinFunction func, int type = 0); static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0); @@ -19,16 +17,19 @@ namespace Components private: static std::string ScriptName; - static std::vector ScriptHandles; static std::unordered_map CustomScrFunctions; static std::unordered_map CustomScrMethods; static std::vector ScriptNameStack; static unsigned short FunctionName; static std::unordered_map ScriptStorage; static std::unordered_map ScriptBaseProgramNum; + static int LastFrameTime; + + static std::vector ScriptMainHandles; + static std::vector ScriptInitHandles; + static std::unordered_map ReplacedFunctions; static const char* ReplacedPos; - static int LastFrameTime; static void CompileError(unsigned int offset, const char* message, ...); static void PrintSourcePos(const char* filename, unsigned int offset); @@ -43,8 +44,9 @@ namespace Components static void RestoreScriptName(); static void RestoreScriptNameStub(); - static void LoadGameType(); - static void LoadGameTypeScript(); + static void Scr_LoadGameType_Stub(); + static void Scr_StartupGameType_Stub(); + static void GScr_LoadGameTypeScript_Stub(); static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type); static Game::BuiltinMethod BuiltIn_GetMethod(const char** pName, int* type); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 15c1be07..d5959113 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -274,6 +274,7 @@ namespace Game RemoveRefToObject_t RemoveRefToObject = RemoveRefToObject_t(0x437190); Scr_LoadGameType_t Scr_LoadGameType = Scr_LoadGameType_t(0x4D9520); + Scr_StartupGameType_t Scr_StartupGameType = Scr_StartupGameType_t(0x438720); Scr_LoadScript_t Scr_LoadScript = Scr_LoadScript_t(0x45D940); Scr_GetFunctionHandle_t Scr_GetFunctionHandle = Scr_GetFunctionHandle_t(0x4234F0); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index bec366d7..b244694b 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -717,9 +717,12 @@ namespace Game typedef void(__cdecl * Scr_ShutdownAllocNode_t)(); extern Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode; - typedef int(__cdecl * Scr_LoadGameType_t)(); + typedef void(__cdecl * Scr_LoadGameType_t)(); extern Scr_LoadGameType_t Scr_LoadGameType; + typedef void(__cdecl * Scr_StartupGameType_t)(); + extern Scr_StartupGameType_t Scr_StartupGameType; + typedef int(__cdecl * Scr_LoadScript_t)(const char*); extern Scr_LoadScript_t Scr_LoadScript; @@ -750,13 +753,13 @@ namespace Game typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char* filename, const char* name); extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle; - typedef int(__cdecl * Scr_ExecThread_t)(int, int); + typedef int(__cdecl * Scr_ExecThread_t)(int handle, unsigned int paramcount); extern Scr_ExecThread_t Scr_ExecThread; typedef int(__cdecl * Scr_ExecEntThread_t)(gentity_s* ent, int handle, unsigned int paramcount); extern Scr_ExecEntThread_t Scr_ExecEntThread; - typedef int(__cdecl * Scr_FreeThread_t)(int); + typedef void(__cdecl * Scr_FreeThread_t)(unsigned __int16 handle); extern Scr_FreeThread_t Scr_FreeThread; typedef void(__cdecl * Scr_Notify_t)(gentity_t *ent, unsigned __int16 stringValue, unsigned int paramcount); From 1bcd1672ece7511564f0846b8132ceabb349f0e7 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 25 Jun 2022 18:21:12 +0200 Subject: [PATCH 04/77] [Node] Use Main pipeline --- src/Components/Modules/Network.cpp | 6 +++--- src/Components/Modules/Node.cpp | 10 +++++----- src/Components/Modules/ServerList.cpp | 9 +++++---- src/Components/Modules/Session.cpp | 2 +- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index b4dbf17a..b30cc5c6 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -283,10 +283,10 @@ namespace Components bool Network::HandleCommand(Game::netadr_t* address, const char* command, const Game::msg_t* message) { - const auto cmd_string = Utils::String::ToLower(command); - const auto handler = Network::Callbacks.find(cmd_string); + const auto command_ = Utils::String::ToLower(command); + const auto handler = Network::Callbacks.find(command_); - const auto offset = cmd_string.size() + 5; + const auto offset = command_.size() + 5; if (static_cast(message->cursize) < offset || handler == Network::Callbacks.end()) { return false; diff --git a/src/Components/Modules/Node.cpp b/src/Components/Modules/Node.cpp index bd6554fd..0b2918bc 100644 --- a/src/Components/Modules/Node.cpp +++ b/src/Components/Modules/Node.cpp @@ -21,7 +21,7 @@ namespace Components return true; } } - else if(this->lastResponse->elapsed(NODE_HALFLIFE * 2) && this->lastRequest.has_value() && this->lastRequest->after(*this->lastResponse)) + else if (this->lastResponse->elapsed(NODE_HALFLIFE * 2) && this->lastRequest.has_value() && this->lastRequest->after(*this->lastResponse)) { return true; } @@ -208,7 +208,7 @@ namespace Components i = Node::Nodes.erase(i); continue; } - else if (sentRequests < queryLimit.get() && i->requiresRequest()) + if (sentRequests < queryLimit.get() && i->requiresRequest()) { ++sentRequests; i->sendRequest(); @@ -326,7 +326,7 @@ namespace Components Logger::Debug("Sending {} nodeListResponse length to {}\n", nodeListData.length(), address.getCString()); #endif Session::Send(address, "nodeListResponse", nodeListData); - }, Scheduler::Pipeline::SERVER, NODE_SEND_RATE * i++); + }, Scheduler::Pipeline::MAIN, NODE_SEND_RATE * i++); } } @@ -346,10 +346,10 @@ namespace Components Node::StoreNodes(false); }, Scheduler::Pipeline::ASYNC); - Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::SERVER); + Scheduler::Loop(Node::RunFrame, Scheduler::Pipeline::MAIN); Session::Handle("nodeListResponse", Node::HandleResponse); - Session::Handle("nodeListRequest", [](const Network::Address& address, const std::string&) + Session::Handle("nodeListRequest", [](const Network::Address& address, [[maybe_unused]] const std::string& data) { Node::SendList(address); }); diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index 5e1aebae..691f64e1 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -27,11 +27,11 @@ namespace Components { return &ServerList::OnlineList; } - else if (ServerList::IsOfflineList()) + if (ServerList::IsOfflineList()) { return &ServerList::OfflineList; } - else if (ServerList::IsFavouriteList()) + if (ServerList::IsFavouriteList()) { return &ServerList::FavouriteList; } @@ -289,6 +289,7 @@ namespace Components { Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort); Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000); + useMasterServer = false; return; } @@ -828,8 +829,8 @@ namespace Components // Set default masterServerName + port and save it Utils::Hook::Set(0x60AD92, "master.xlabs.dev"); - Utils::Hook::Set(0x60AD90, Game::dvar_flag::DVAR_ARCHIVE); // masterServerName - Utils::Hook::Set(0x60ADC6, Game::dvar_flag::DVAR_ARCHIVE); // masterPort + Utils::Hook::Set(0x60AD90, Game::dvar_flag::DVAR_NONE); // masterServerName + Utils::Hook::Set(0x60ADC6, Game::dvar_flag::DVAR_NONE); // masterPort // Add server list feeder UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer); diff --git a/src/Components/Modules/Session.cpp b/src/Components/Modules/Session.cpp index 1f575184..cbbe42be 100644 --- a/src/Components/Modules/Session.cpp +++ b/src/Components/Modules/Session.cpp @@ -37,7 +37,7 @@ namespace Components { Network::SendCommand(delayData->target, delayData->command, delayData->data); delete delayData; - }, Scheduler::Pipeline::SERVER, 500ms + std::chrono::milliseconds(std::rand() % 200)); + }, Scheduler::Pipeline::MAIN, 500ms + std::chrono::milliseconds(std::rand() % 200)); #else std::lock_guard _(Session::Mutex); From ffd374fcd6e738176bda4187b9844ee83b821a86 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 25 Jun 2022 20:17:34 +0200 Subject: [PATCH 05/77] [Structs] Document scr_const_t --- src/Game/Functions.cpp | 2 + src/Game/Functions.hpp | 2 + src/Game/Structs.hpp | 261 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 15c1be07..4a4bf52e 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -596,6 +596,8 @@ namespace Game int* com_errorPrintsCount = reinterpret_cast(0x1AD7910); + scr_const_t* scr_const = reinterpret_cast(0x1AA2E00); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index bec366d7..fd611c21 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1231,6 +1231,8 @@ namespace Game extern int* com_errorPrintsCount; + extern scr_const_t* scr_const; + void Sys_LockRead(FastCriticalSection* critSect); void Sys_UnlockRead(FastCriticalSection* critSect); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 16a40cdd..f7f50331 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -5360,6 +5360,267 @@ namespace Game static_assert(sizeof(scrVarPub_t) == 0x24060); + struct scr_const_t + { + scr_string_t _; + scr_string_t active; + scr_string_t aim_bone; + scr_string_t aim_highest_bone; + scr_string_t aim_vis_bone; + scr_string_t all; + scr_string_t allies; + scr_string_t angles; + scr_string_t auto_ai; + scr_string_t auto_nonai; + scr_string_t axis; + scr_string_t back; + scr_string_t bad_guys; + scr_string_t bad_path; + scr_string_t begin_firing; + scr_string_t begin_firing_left; + scr_string_t cancel_location; + scr_string_t chest; + scr_string_t confirm_location; + scr_string_t crouch; + scr_string_t current; + scr_string_t damage; + scr_string_t dead; + scr_string_t death; + scr_string_t deathshield; + scr_string_t detonate; + scr_string_t direct; + scr_string_t dlight; + scr_string_t done; + scr_string_t empty; + scr_string_t empty_offhand; + scr_string_t offhand_end; + scr_string_t end_firing; + scr_string_t end_firing_left; + scr_string_t entity; + scr_string_t explode; + scr_string_t failed; + scr_string_t first_person; + scr_string_t forward; + scr_string_t fraction; + scr_string_t free; + scr_string_t goal; + scr_string_t goal_changed; + scr_string_t goal_yaw; + scr_string_t grenade; + scr_string_t grenadedanger; + scr_string_t grenade_fire; + scr_string_t glass_destroyed; + scr_string_t missile_fire; + scr_string_t grenade_pullback; + scr_string_t missile_stuck; + scr_string_t info_notnull; + scr_string_t invisible; + scr_string_t key1; + scr_string_t key2; + scr_string_t killanimscript; + scr_string_t left; + scr_string_t light; + scr_string_t manual; + scr_string_t manual_ai; + scr_string_t movedone; + scr_string_t none; + scr_string_t normal; + scr_string_t origin; + scr_string_t other; + scr_string_t player; + scr_string_t physics_finished; + scr_string_t position; + scr_string_t projectile_impact; + scr_string_t prone; + scr_string_t right; + scr_string_t reload; + scr_string_t reload_start; + scr_string_t result; + scr_string_t reverse; + scr_string_t rocket; + scr_string_t rotatedone; + scr_string_t script_brushmodel; + scr_string_t script_model; + scr_string_t script_origin; + scr_string_t sentry; + scr_string_t sentry_offline; + scr_string_t snd_enveffectsprio_level; + scr_string_t snd_enveffectsprio_shellshock; + scr_string_t snd_channelvolprio_holdbreath; + scr_string_t snd_channelvolprio_pain; + scr_string_t snd_channelvolprio_shellshock; + scr_string_t spawned; + scr_string_t stand; + scr_string_t suppression; + scr_string_t suppression_end; + scr_string_t surfacetype; + scr_string_t tag_aim; + scr_string_t tag_aim_animated; + scr_string_t tag_aim_pivot; + scr_string_t tag_brass; + scr_string_t tag_butt; + scr_string_t tag_clip; + scr_string_t tag_eye; + scr_string_t tag_flash; + scr_string_t tag_flash_silenced; + scr_string_t tag_flash_11; + scr_string_t tag_flash_2; + scr_string_t tag_flash_22; + scr_string_t tag_flash_3; + scr_string_t tag_fx; + scr_string_t tag_inhand; + scr_string_t tag_knife_fx; + scr_string_t tag_laser; + scr_string_t tag_origin; + scr_string_t tag_weapon; + scr_string_t tag_player; + scr_string_t tag_camera; + scr_string_t tag_weapon_left; + scr_string_t tag_weapon_right; + scr_string_t tag_weapon_chest; + scr_string_t tag_stowed_back; + scr_string_t tag_gasmask; + scr_string_t tag_gasmask2; + scr_string_t tag_sync; + scr_string_t tag_motion_tracker_tl; + scr_string_t tag_motion_tracker_bl; + scr_string_t tag_motion_tracker_br; + scr_string_t tag_motion_tracker_fx; + scr_string_t tag_reticle_acog; + scr_string_t tag_reticle_red_dot; + scr_string_t tag_reticle_tavor_scope; + scr_string_t tag_reticle_thermal_scope; + scr_string_t tag_eotech_reticle; + scr_string_t target_script_trigger; + scr_string_t third_person; + scr_string_t top; + scr_string_t touch; + scr_string_t trigger; + scr_string_t trigger_use; + scr_string_t trigger_use_touch; + scr_string_t trigger_damage; + scr_string_t truck_cam; + scr_string_t weapon_change; + scr_string_t weapon_fired; + scr_string_t weapon_switch_started; + scr_string_t weapon_taken; + scr_string_t weapon_dropped; + scr_string_t worldspawn; + scr_string_t flashbang; + scr_string_t flash; + scr_string_t smoke; + scr_string_t frag; + scr_string_t throwingknife; + scr_string_t night_vision_on; + scr_string_t night_vision_off; + scr_string_t mod_unknown; + scr_string_t mod_pistol_bullet; + scr_string_t mod_rifle_bullet; + scr_string_t mod_explosive_bullet; + scr_string_t mod_grenade; + scr_string_t mod_grenade_splash; + scr_string_t mod_projectile; + scr_string_t mod_projectile_splash; + scr_string_t mod_melee; + scr_string_t mod_head_shot; + scr_string_t mod_crush; + scr_string_t mod_falling; + scr_string_t mod_suicide; + scr_string_t mod_trigger_hurt; + scr_string_t mod_explosive; + scr_string_t mod_impact; + scr_string_t script_vehicle; + scr_string_t script_vehicle_collision; + scr_string_t script_vehicle_collmap; + scr_string_t script_vehicle_corpse; + scr_string_t turret_deactivate; + scr_string_t turret_fire; + scr_string_t turret_no_vis; + scr_string_t turret_not_on_target; + scr_string_t turret_on_target; + scr_string_t turret_on_vistarget; + scr_string_t turret_pitch_clamped; + scr_string_t turret_rotate_stopped; + scr_string_t turret_yaw_clamped; + scr_string_t turretstatechange; + scr_string_t turretownerchange; + scr_string_t reached_end_node; + scr_string_t reached_wait_node; + scr_string_t reached_wait_speed; + scr_string_t near_goal; + scr_string_t tag_wheel_front_left; + scr_string_t tag_wheel_front_right; + scr_string_t tag_wheel_back_left; + scr_string_t tag_wheel_back_right; + scr_string_t tag_wheel_middle_left; + scr_string_t tag_wheel_middle_right; + scr_string_t tag_detach; + scr_string_t tag_popout; + scr_string_t tag_body; + scr_string_t tag_turret; + scr_string_t tag_turret_base; + scr_string_t tag_barrel; + scr_string_t front_left; + scr_string_t front_right; + scr_string_t back_left; + scr_string_t back_right; + scr_string_t middle_left; + scr_string_t middle_right; + scr_string_t veh_boatbounce; + scr_string_t veh_collision; + scr_string_t veh_predictedcollision; + scr_string_t veh_leftground; + scr_string_t veh_landed; + scr_string_t veh_jolt; + scr_string_t vehicle_mount; + scr_string_t vehicle_dismount; + scr_string_t constrained; + scr_string_t follow; + scr_string_t j_head; + scr_string_t j_neck; + scr_string_t thermal; + scr_string_t primary; + scr_string_t offhand; + scr_string_t item; + scr_string_t altmode; + scr_string_t exclusive; + scr_string_t scavenger; + scr_string_t primaryoffhand; + scr_string_t secondaryoffhand; + scr_string_t actionslot1; + scr_string_t actionslot2; + scr_string_t actionslot3; + scr_string_t actionslot4; + scr_string_t back_low; + scr_string_t back_mid; + scr_string_t back_up; + scr_string_t pelvis; + scr_string_t auto_change; + scr_string_t begin; + scr_string_t call_vote; + scr_string_t freelook; + scr_string_t intermission; + scr_string_t j_mainroot; + scr_string_t manual_change; + scr_string_t menuresponse; + scr_string_t pistol; + scr_string_t plane_waypoint; + scr_string_t playing; + scr_string_t spectator; + scr_string_t spectating_cycle; + scr_string_t vote; + scr_string_t sprint_begin; + scr_string_t sprint_end; + scr_string_t normal_radar; + scr_string_t fast_radar; + scr_string_t tag_engine_left; + scr_string_t tag_engine_right; + scr_string_t slowmo_active; + scr_string_t slowmo_passive; + }; + + static_assert(sizeof(scr_const_t) == 0x1FE); + enum UILocalVarType { UILOCALVAR_INT = 0x0, From 7b67a6d0877e181ee4ab118606a0c1ba6bbf5a79 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 25 Jun 2022 20:22:13 +0200 Subject: [PATCH 06/77] DisableWeaponPickup & EnableWeaponPickup --- src/Components/Modules/AssetHandler.cpp | 2 +- src/Components/Modules/MapRotation.cpp | 8 ++-- src/Components/Modules/Weapon.cpp | 58 +++++++++++++++++++++++-- src/Components/Modules/Weapon.hpp | 3 ++ src/Game/Structs.hpp | 4 ++ 5 files changed, 67 insertions(+), 8 deletions(-) diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index a65259e2..1f2f4161 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -1,4 +1,4 @@ -#include +#include #include "AssetInterfaces/IFont_s.hpp" #include "AssetInterfaces/IWeapon.hpp" diff --git a/src/Components/Modules/MapRotation.cpp b/src/Components/Modules/MapRotation.cpp index 30adf749..8c6658c4 100644 --- a/src/Components/Modules/MapRotation.cpp +++ b/src/Components/Modules/MapRotation.cpp @@ -226,11 +226,11 @@ namespace Components // Ook, ook, eek Logger::Warning(Game::CON_CHANNEL_SERVER, "You are using deprecated {}", (*SVMapRotationCurrent)->name); - RotationData currentRotation; + RotationData rotationCurrent; try { Logger::Debug("Parsing {}", (*SVMapRotationCurrent)->name); - currentRotation.parse(data); + rotationCurrent.parse(data); } catch (const std::exception& ex) { @@ -239,14 +239,14 @@ namespace Components Game::Dvar_SetString(*SVMapRotationCurrent, ""); - if (currentRotation.getEntriesSize() == 0) + if (rotationCurrent.getEntriesSize() == 0) { Logger::Print(Game::CON_CHANNEL_SERVER, "{} is empty or contains invalid data. Restarting map\n", (*SVMapRotationCurrent)->name); RestartCurrentMap(); return; } - ApplyRotation(currentRotation); + ApplyRotation(rotationCurrent); } void MapRotation::RandomizeMapRotation() diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index 1b9719a3..82e2db4d 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -448,10 +448,57 @@ namespace Components } } + void __declspec(naked) Weapon::WeaponEntCanBeGrabbed_Stub() + { + using namespace Game; + + __asm + { + cmp dword ptr [esp + 0x8], 0x0 + jz touched + + push 0x56E82C + retn + + touched: + test dword ptr [edi + 0x2BC], PWF_DISABLE_WEAPON_PICKUP + jnz exit_func + + // Game code + test eax, eax + jz continue_func + + exit_func: + xor eax, eax + ret + + continue_func: + push 0x56E84C + retn + } + } + + void Weapon::AddScriptMethods() + { + Script::AddMethod("DisableWeaponPickup", [](Game::scr_entref_t entref) + { + const auto* ent = Game::GetPlayerEntity(entref); + + ent->client->ps.weapCommon.weapFlags |= Game::PWF_DISABLE_WEAPON_PICKUP; + }); + + Script::AddMethod("EnableWeaponPickup", [](Game::scr_entref_t entref) + { + const auto* ent = Game::GetPlayerEntity(entref); + + ent->client->ps.weapCommon.weapFlags &= ~Game::PWF_DISABLE_WEAPON_PICKUP; + }); + } + Weapon::Weapon() { - Weapon::PatchLimit(); - Weapon::PatchConfigStrings(); + PatchLimit(); + PatchConfigStrings(); // Intercept weapon loading AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_WEAPON, Weapon::WeaponFileLoad); @@ -466,7 +513,7 @@ namespace Components // Weapon swap fix Utils::Hook::Nop(0x4B3670, 5); - Utils::Hook(0x57B4F0, LoadNoneWeaponHookStub).install()->quick(); + Utils::Hook(0x57B4F0, LoadNoneWeaponHookStub, HOOK_JUMP).install()->quick(); // Don't load bounce sounds for now, it causes crashes // TODO: Actually check the weaponfiles and/or reset the soundtable correctly! @@ -477,5 +524,10 @@ namespace Components // Clear weapons independently from fs_game //Utils::Hook::Nop(0x452C1D, 2); //Utils::Hook::Nop(0x452C24, 5); + + AddScriptMethods(); + + AssertOffset(Game::playerState_s, Game::playerState_s::weapCommon.weapFlags, 0x2BC); + Utils::Hook(0x56E825, WeaponEntCanBeGrabbed_Stub, HOOK_JUMP).install()->quick(); } } diff --git a/src/Components/Modules/Weapon.hpp b/src/Components/Modules/Weapon.hpp index 3bd764f6..12dd99a2 100644 --- a/src/Components/Modules/Weapon.hpp +++ b/src/Components/Modules/Weapon.hpp @@ -25,5 +25,8 @@ namespace Components static void ParseConfigStrings(); static int ParseWeaponConfigStrings(); static int ClearConfigStrings(void* dest, int value, int size); + + static void WeaponEntCanBeGrabbed_Stub(); + static void AddScriptMethods(); }; } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 16a40cdd..06b420b2 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -1367,6 +1367,8 @@ namespace Game PWF_DISABLE_WEAPON_SWAPPING = 1 << 11, PWF_DISABLE_OFFHAND_WEAPONS = 1 << 12, PWF_SWITCHING_TO_RIOTSHIELD = 1 << 13, + // IW5 flags backported + PWF_DISABLE_WEAPON_PICKUP = 1 << 16 }; struct playerState_s @@ -1491,6 +1493,8 @@ namespace Game int stunTime; }; + static_assert(sizeof(Game::playerState_s) == 0x311C); + enum LocSelInputState { LOC_SEL_INPUT_NONE = 0x0, From d747a9c670a949c0e1ac0a36e28c31bfe33e3234 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 25 Jun 2022 20:23:13 +0200 Subject: [PATCH 07/77] [AssetHandler] Fix tabs --- src/Components/Modules/AssetHandler.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 1f2f4161..24dc0051 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -518,10 +518,10 @@ namespace Components Utils::Hook::Set(0x5BAEA2, entryPool + 1); } - void AssetHandler::ExposeTemporaryAssets(bool expose) - { - AssetHandler::ShouldSearchTempAssets = expose; - } + void AssetHandler::ExposeTemporaryAssets(bool expose) + { + AssetHandler::ShouldSearchTempAssets = expose; + } AssetHandler::AssetHandler() { From 02dd8f9d401e462c3097f7c17c21c26b8e860cdf Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 12:26:18 +0200 Subject: [PATCH 08/77] [Ceg] One more --- src/Components/Modules/Ceg.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index de6c33df..487ad4ba 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -47,5 +47,6 @@ namespace Components Utils::Hook::Set(0x49E8C0, 0xC3); Utils::Hook::Set(0x42DB00, 0xC3); Utils::Hook::Set(0x4F4CF0, 0xC3); + Utils::Hook::Set(0x432180, 0xC3); } } From 4ed20d711180e8b407216e20cbb50ae7ad7a66e6 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 12:59:30 +0200 Subject: [PATCH 09/77] [Ceg] Remove duplicates --- src/Components/Modules/Ceg.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index 487ad4ba..907a9e20 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -36,10 +36,6 @@ namespace Components Utils::Hook::Nop(0x43EC96, 9); Utils::Hook::Nop(0x4675C6, 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); @@ -48,5 +44,6 @@ namespace Components Utils::Hook::Set(0x42DB00, 0xC3); Utils::Hook::Set(0x4F4CF0, 0xC3); Utils::Hook::Set(0x432180, 0xC3); + Utils::Hook::Set(0x461930, 0xC3); } } From 998c221d8012a7888adfd04bf184f90f2288f725 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 14:58:45 +0200 Subject: [PATCH 10/77] [Dvars] Add flag to protect archive dvars --- README.md | 1 + src/Components/Modules/Dvar.cpp | 21 +++++++++++++++++++++ src/Components/Modules/Dvar.hpp | 1 + 3 files changed, 23 insertions(+) diff --git a/README.md b/README.md index e13fbcc9..16ebf5cd 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ | `-version` | Print IW4x build info on startup. | | `-zonebuilder` | Start the interactive zonebuilder tool console instead of starting the game. | | `-nosteam` | Disable friends feature and do not update Steam about the game's current status just like an invisible mode. | +| `-protect-saved-dvars` | Block the server from modifying saved/archive dvars. | ## Disclaimer diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index bb29d9fb..5a8fb845 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -283,6 +283,18 @@ namespace Components Game::Dvar_SetFromStringByNameFromSource(dvarName, string, Game::DvarSetSource::DVAR_SOURCE_EXTERNAL); } + bool Dvar::AreArchiveDvarsProtected() + { + static std::optional flag; + + if (!flag.has_value()) + { + flag.emplace(Flags::HasFlag("protect-saved-dvars")); + } + + return flag.value(); + } + void Dvar::SaveArchiveDvar(const Game::dvar_t* var) { if (!Utils::IO::FileExists(Dvar::ArchiveDvarPath)) @@ -301,6 +313,15 @@ namespace Components const auto* dvar = Game::Dvar_FindVar(dvarName); if (dvar != nullptr && dvar->flags & Game::dvar_flag::DVAR_ARCHIVE) { + if (Dvar::AreArchiveDvarsProtected()) + { + Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Not allowing server to override saved dvar '{}'\n", dvarName); + return; + } + +#ifdef DEBUG_DVARS + Logger::Print(Game::CON_CHANNEL_CONSOLEONLY, "Server is overriding saved dvar '{}'\n", dvarName); +#endif Dvar::SaveArchiveDvar(dvar); } diff --git a/src/Components/Modules/Dvar.hpp b/src/Components/Modules/Dvar.hpp index 3a184db0..fb51fbcb 100644 --- a/src/Components/Modules/Dvar.hpp +++ b/src/Components/Modules/Dvar.hpp @@ -57,6 +57,7 @@ namespace Components static void SetFromStringByNameExternal(const char* dvar, const char* value); static void SetFromStringByNameSafeExternal(const char* dvar, const char* value); + static bool AreArchiveDvarsProtected(); static void SaveArchiveDvar(const Game::dvar_t* var); static void DvarSetFromStringByNameStub(const char* dvarName, const char* value); }; From 8d2db6b34d03ff8fafb571e105ed0874bbcdf249 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 15:40:57 +0200 Subject: [PATCH 11/77] [Utils] Organize Vector functions under the maths module --- src/Components/Modules/Renderer.cpp | 12 ++++---- src/Game/Functions.cpp | 42 +++++++------------------- src/Game/Functions.hpp | 14 +++++++-- src/STDInclude.hpp | 1 + src/Utils/Maths.cpp | 46 +++++++++++++++++++++++++++++ src/Utils/Maths.hpp | 15 ++++++++++ src/Utils/Utils.cpp | 11 ------- src/Utils/Utils.hpp | 12 +++----- 8 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 src/Utils/Maths.cpp create mode 100644 src/Utils/Maths.hpp diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index 28feacf3..dd18f02a 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -284,7 +284,7 @@ namespace Components if (!scene->sceneModel[i].model) continue; - if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist) { auto b = scene->sceneModel[i].model->bounds; b.midPoint[0] += scene->sceneModel[i].placement.base.origin[0]; @@ -302,7 +302,7 @@ namespace Components for (auto i = 0; i < scene->sceneDObjCount; i++) { - if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].cull.bounds.midPoint) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].cull.bounds.midPoint) < sqrDist) { scene->sceneDObj[i].cull.bounds.halfSize[0] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[0]); scene->sceneDObj[i].cull.bounds.halfSize[1] = std::abs(scene->sceneDObj[i].cull.bounds.halfSize[1]); @@ -327,7 +327,7 @@ namespace Components { auto staticModel = world->dpvs.smodelDrawInsts[i]; - if (Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist) { if (staticModel.model) { @@ -381,7 +381,7 @@ namespace Components if (!scene->sceneModel[i].model) continue; - if (Utils::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist) { Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name); } @@ -395,7 +395,7 @@ namespace Components { for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++) { - if (Utils::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < sqrDist) { Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name); } @@ -411,7 +411,7 @@ namespace Components auto staticModel = world->dpvs.smodelDrawInsts[i]; if (staticModel.model) { - auto dist = Utils::Vec3SqrDistance(playerPosition, staticModel.placement.origin); + const auto dist = Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin); if (dist < sqrDist) { Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 4a4bf52e..fbcd5845 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -451,6 +451,11 @@ namespace Game player_die_t player_die = player_die_t(0x42BC70); + Vec3Normalize_t Vec3Normalize = Vec3Normalize_t(0x453500); + Vec3NormalizeFast_t Vec3NormalizeFast = Vec3NormalizeFast_t(0x478F80); + Vec2Normalize_t Vec2Normalize = Vec2Normalize_t(0x416F70); + Vec2NormalizeFast_t Vec2NormalizeFast = Vec2NormalizeFast_t(0x5FC830); + XAssetHeader* DB_XAssetPool = reinterpret_cast(0x7998A8); unsigned int* g_poolSize = reinterpret_cast(0x7995E8); @@ -918,33 +923,6 @@ namespace Game return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7)); } - float Vec2Normalize(vec2_t& vec) - { - const auto length = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1]); - - if (length > 0.0f) - { - vec[0] /= length; - vec[1] /= length; - } - - return length; - } - - float Vec3Normalize(vec3_t& vec) - { - const auto length = std::sqrt(vec[0] * vec[0] + vec[1] * vec[1] + vec[2] * vec[2]); - - if (length > 0.0f) - { - vec[0] /= length; - vec[1] /= length; - vec[2] /= length; - } - - return length; - } - void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out) { unsigned int v3; // xmm1_4 @@ -1682,11 +1660,11 @@ namespace Game pushad mov eax, [esp + 0x4 + 0x20] // dvar - push[esp + 0x18 + 0x20] // source - push[esp + 0x18 + 0x20] // value - push[esp + 0x18 + 0x20] // value - push[esp + 0x18 + 0x20] // value - push[esp + 0x18 + 0x20] // value + push [esp + 0x18 + 0x20] // source + push [esp + 0x18 + 0x20] // value + push [esp + 0x18 + 0x20] // value + push [esp + 0x18 + 0x20] // value + push [esp + 0x18 + 0x20] // value mov ebx, 0x647400 call ebx diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index fd611c21..4e45d8ab 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1080,6 +1080,18 @@ namespace Game typedef void(__cdecl * player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, int iWeapon, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset); extern player_die_t player_die; + typedef float(__cdecl * Vec3Normalize_t)(float* v); + extern Vec3Normalize_t Vec3Normalize; + + typedef void(__cdecl * Vec3NormalizeFast_t)(float* v); + extern Vec3NormalizeFast_t Vec3NormalizeFast; + + typedef float(__cdecl * Vec2Normalize_t)(float* v); + extern Vec2Normalize_t Vec2Normalize; + + typedef void(__cdecl * Vec2NormalizeFast_t)(float* v); + extern Vec2NormalizeFast_t Vec2NormalizeFast; + extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; @@ -1295,8 +1307,6 @@ namespace Game void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format); - float Vec2Normalize(vec2_t& vec); - float Vec3Normalize(vec3_t& vec); void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out); void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution); void QuatRot(vec3_t* vec, const vec4_t* quat); diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 63e5d202..950c5248 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -128,6 +128,7 @@ using namespace std::literals; #include "Utils/IO.hpp" #include "Utils/Json.hpp" #include "Utils/Library.hpp" +#include "Utils/Maths.hpp" #include "Utils/String.hpp" #include "Utils/Thread.hpp" #include "Utils/Time.hpp" diff --git a/src/Utils/Maths.cpp b/src/Utils/Maths.cpp new file mode 100644 index 00000000..2b840002 --- /dev/null +++ b/src/Utils/Maths.cpp @@ -0,0 +1,46 @@ +#include + +namespace Utils::Maths +{ + float DotProduct(float v1[3], float v2[3]) + { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; + } + + void VectorSubtract(float va[3], float vb[3], float out[3]) + { + out[0] = va[0] - vb[0]; + out[1] = va[1] - vb[1]; + out[2] = va[2] - vb[2]; + } + + void VectorAdd(float va[3], float vb[3], float out[3]) + { + out[0] = va[0] + vb[0]; + out[1] = va[1] + vb[1]; + out[2] = va[2] + vb[2]; + } + + void VectorCopy(float in[3], float out[3]) + { + out[0] = in[0]; + out[1] = in[1]; + out[2] = in[2]; + } + + void VectorScale(float v[3], float scale, float out[3]) + { + out[0] = v[0] * scale; + out[1] = v[1] * scale; + out[2] = v[2] * scale; + } + + float Vec3SqrDistance(float v1[3], float v2[3]) + { + float out[3]; + + VectorSubtract(v2, v1, out); + + return (out[0] * out[0]) + (out[1] * out[1]) + (out[2] * out[2]); + } +} diff --git a/src/Utils/Maths.hpp b/src/Utils/Maths.hpp new file mode 100644 index 00000000..47f37ed5 --- /dev/null +++ b/src/Utils/Maths.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace Utils::Maths +{ + // Macros in Quake-III + constexpr auto VectorClear(float x[3]) { x[0] = x[1] = x[2] = 0; } + constexpr auto VectorNegate(float x[3]) { x[0] = -x[0]; x[1] = -x[1]; x[2] = -x[2]; } + + float DotProduct(float v1[3], float v2[3]); + void VectorSubtract(float va[3], float vb[3], float out[3]); + void VectorAdd(float va[3], float vb[3], float out[3]); + void VectorCopy(float in[3], float out[3]); + void VectorScale(float v[3], float scale, float out[3]); + float Vec3SqrDistance(float v1[3], float v2[3]); +} diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index 5166b84f..a214c963 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -145,15 +145,4 @@ namespace Utils { return !(base1 + len1 <= base2 || base2 + len2 <= base1); } - - float Vec3SqrDistance(const float v1[3], const float v2[3]) - { - auto x = v2[0] - v1[0]; - auto y = v2[1] - v1[1]; - auto z = v2[2] - v1[2]; - - return x * x + y * y + z * z; - } - - } diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index cca25872..79c6b706 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -24,9 +24,8 @@ namespace Utils void OpenUrl(const std::string& url); bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2); - float Vec3SqrDistance(const float v1[3], const float v2[3]); - template inline void RotLeft(T& object, size_t bits) + template void RotLeft(T& object, size_t bits) { bits %= sizeof(T) * 8; @@ -39,13 +38,13 @@ namespace Utils object |= T(negative) << ((sizeof(T) * 8 - 1 + bits) % (sizeof(T) * 8)); } - template inline void RotRight(T& object, size_t bits) + template void RotRight(T& object, size_t bits) { bits %= (sizeof(T) * 8); RotLeft(object, ((sizeof(T) * 8) - bits)); } - template inline void Merge(std::vector* target, T* source, size_t length) + template void Merge(std::vector* target, T* source, size_t length) { if (source) { @@ -56,7 +55,7 @@ namespace Utils } } - template inline void Merge(std::vector* target, std::vector source) + template void Merge(std::vector* target, std::vector source) { for (auto &entry : source) { @@ -122,7 +121,4 @@ 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]; } } From 8ee1bc1ed971dedc6e8f7b15946aea5e05edcf41 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 20:20:11 +0200 Subject: [PATCH 12/77] [Logger] Fix output by g_log_list & log_list --- src/Components/Modules/Logger.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index d04a5d31..2a25e37e 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -265,7 +265,7 @@ namespace Components { if (params->size() < 2) return; - int num = atoi(params->get(1)); + const auto num = atoi(params->get(1)); if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[0].size()) { auto addr = Logger::LoggingAddresses[0].begin() + num; @@ -296,7 +296,7 @@ namespace Components for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i) { - Logger::Print("{}: {}\n", i, Logger::LoggingAddresses[0][i].getCString()); + Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[0][i].getCString()); } }); @@ -316,7 +316,7 @@ namespace Components { if (params->size() < 2) return; - int num = atoi(params->get(1)); + const auto num = std::atoi(params->get(1)); if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[1].size()) { const auto addr = Logger::LoggingAddresses[1].begin() + num; @@ -347,7 +347,7 @@ namespace Components for (std::size_t i = 0; i < Logger::LoggingAddresses[1].size(); ++i) { - Logger::Print("{}: {}\n", i, Logger::LoggingAddresses[1][i].getCString()); + Logger::Print("#{:03d}: {}\n", i, Logger::LoggingAddresses[1][i].getCString()); } }); } From e2170e96c21c6b2d00a52b9c8dfdfd8af217cff0 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 21:36:23 +0200 Subject: [PATCH 13/77] [Logger] Decouple NetworkLog from the logFile --- src/Components/Modules/Logger.cpp | 36 ++++++++++++++++++------------ src/Components/Modules/Logger.hpp | 2 +- src/Components/Modules/Theatre.cpp | 12 +++++----- src/Game/Functions.cpp | 3 ++- src/Game/Functions.hpp | 7 ++++-- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 2a25e37e..2cef7c80 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -153,22 +153,30 @@ namespace Components } } - __declspec(naked) void Logger::GameLogStub() + void Logger::G_LogPrintfStub(const char* fmt, ...) { - __asm + char string[1024]; + char string2[1024]; + + va_list ap; + va_start(ap, fmt); + vsnprintf_s(string2, _TRUNCATE, fmt, ap); + va_end(ap); + + const auto min = Game::level->time / 1000 / 60; + const auto tens = Game::level->time / 1000 % 60 / 10; + const auto sec = Game::level->time / 1000 % 60 % 10; + + _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", + min, tens, sec, string2); + + if (Game::level->logFile != nullptr) { - pushad - - push 1 - push [esp + 28h] - call Logger::NetworkLog - add esp, 8h - - popad - - push 4576C0h - retn + Game::FS_Write(string, &string[std::strlen(string) + 1] - &string[1], reinterpret_cast(Game::level->logFile)); } + + // Allow the network log to run even if logFile was not opened + Logger::NetworkLog(string, true); } __declspec(naked) void Logger::PrintMessageStub() @@ -361,7 +369,7 @@ namespace Components Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER); - Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick(); + Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintfStub, HOOK_JUMP).install()->quick(); Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); if (Loader::IsPerformingUnitTests()) diff --git a/src/Components/Modules/Logger.hpp b/src/Components/Modules/Logger.hpp index 5d3b7fa7..88f9f1da 100644 --- a/src/Components/Modules/Logger.hpp +++ b/src/Components/Modules/Logger.hpp @@ -100,7 +100,7 @@ namespace Components static void(*PipeCallback)(const std::string&); static void Frame(); - static void GameLogStub(); + static void G_LogPrintfStub(const char* fmt, ...); static void PrintMessageStub(); static void PrintMessagePipe(const char* data); static void EnqueueMessage(const std::string& message); diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index 4acc40e4..855a54e9 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -19,7 +19,7 @@ namespace Components void Theatre::RecordGamestateStub() { int sequence = (*Game::serverMessageSequence - 1); - Game::FS_Write(&sequence, 4, *Game::demoFile); + Game::FS_WriteToDemo(&sequence, 4, *Game::demoFile); } void Theatre::StoreBaseline(PBYTE snapshotMsg) @@ -62,10 +62,10 @@ namespace Components int byte8 = 8; char byte0 = 0; - Game::FS_Write(&byte0, 1, *Game::demoFile); - Game::FS_Write(Game::serverMessageSequence, 4, *Game::demoFile); - Game::FS_Write(&fileCompressedSize, 4, *Game::demoFile); - Game::FS_Write(&byte8, 4, *Game::demoFile); + Game::FS_WriteToDemo(&byte0, 1, *Game::demoFile); + Game::FS_WriteToDemo(Game::serverMessageSequence, 4, *Game::demoFile); + Game::FS_WriteToDemo(&fileCompressedSize, 4, *Game::demoFile); + Game::FS_WriteToDemo(&byte8, 4, *Game::demoFile); for (int i = 0; i < compressedSize; i += 1024) { @@ -77,7 +77,7 @@ namespace Components break; } - Game::FS_Write(&cmpData[i], size, *Game::demoFile); + Game::FS_WriteToDemo(&cmpData[i], size, *Game::demoFile); } } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 4a4bf52e..1f6db33d 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -148,7 +148,8 @@ namespace Game FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread = FS_FOpenFileReadForThread_t(0x643270); FS_FCloseFile_t FS_FCloseFile = FS_FCloseFile_t(0x462000); FS_WriteFile_t FS_WriteFile = FS_WriteFile_t(0x426450); - FS_Write_t FS_Write = FS_Write_t(0x4C06E0); + FS_WriteToDemo_t FS_WriteToDemo = FS_WriteToDemo_t(0x4C06E0); + FS_Write_t FS_Write = FS_Write_t(0x4576C0); FS_Printf_t FS_Printf = FS_Printf_t(0x459320); FS_Read_t FS_Read = FS_Read_t(0x4A04C0); FS_Seek_t FS_Seek = FS_Seek_t(0x4A63D0); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index fd611c21..9d5c67c3 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -365,10 +365,13 @@ namespace Game typedef bool(__cdecl * FS_FileExists_t)(const char* file); extern FS_FileExists_t FS_FileExists; - typedef bool(__cdecl * FS_WriteFile_t)(char* filename, char* folder, void* buffer, int size); + typedef bool(__cdecl * FS_WriteFile_t)(const char* filename, const char* folder, const void* buffer, int size); extern FS_WriteFile_t FS_WriteFile; - typedef int(__cdecl * FS_Write_t)(const void* buffer, size_t size, int file); + typedef int(__cdecl * FS_WriteToDemo_t)(const void* buffer, int size, int file); + extern FS_WriteToDemo_t FS_WriteToDemo; + + typedef int(__cdecl * FS_Write_t)(const void* buffer, int len, int h); extern FS_Write_t FS_Write; typedef int(__cdecl * FS_Printf_t)(int file, const char* fmt, ...); From f7cb8da3525bdf9c9f80d8aee8b4ac2994e6d694 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 26 Jun 2022 22:01:11 +0200 Subject: [PATCH 14/77] [Logger] Recycle length from _snprintf_ --- src/Components/Modules/Logger.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 2cef7c80..34c9c0b4 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -144,12 +144,14 @@ namespace Components void Logger::NetworkLog(const char* data, bool gLog) { - if (!data) return; + if (data == nullptr) + { + return; + } - const std::string buffer(data); for (const auto& addr : Logger::LoggingAddresses[gLog & 1]) { - Network::SendCommand(addr, "print", buffer); + Network::SendCommand(addr, "print", data); } } @@ -167,12 +169,12 @@ namespace Components const auto tens = Game::level->time / 1000 % 60 / 10; const auto sec = Game::level->time / 1000 % 60 % 10; - _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", + const auto len = _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", min, tens, sec, string2); if (Game::level->logFile != nullptr) { - Game::FS_Write(string, &string[std::strlen(string) + 1] - &string[1], reinterpret_cast(Game::level->logFile)); + Game::FS_Write(string, len, reinterpret_cast(Game::level->logFile)); } // Allow the network log to run even if logFile was not opened From ef15522a3d309ec57908f487b388ca7bd66d22cf Mon Sep 17 00:00:00 2001 From: Diavolo Date: Mon, 27 Jun 2022 13:02:00 +0200 Subject: [PATCH 15/77] [Toast] Fix --- src/Components/Modules/Toast.cpp | 96 +++++++++++++++++--------------- src/Components/Modules/Toast.hpp | 9 +-- 2 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index 669b5231..58f07398 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -5,30 +5,16 @@ namespace Components std::queue Toast::Queue; std::mutex Toast::Mutex; - void Toast::Show(const std::string& image, const std::string& title, const std::string& description, int length, Utils::Slot callback) + void Toast::Show(const std::string& image, const std::string& title, const std::string& description, int length, const Utils::Slot& callback) { Game::Material* material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material; return Show(material, title, description, length, callback); } - void Toast::Show(Game::Material* material, const std::string& title, const std::string& description, int length, Utils::Slot callback) + void Toast::Show(Game::Material* material, const std::string& title, const std::string& description, int length, const Utils::Slot& callback) { - Toast::Mutex.lock(); - Toast::Queue.push({ material, Utils::String::ToUpper(title), description, length, 0, callback }); - Toast::Mutex.unlock(); - } - - std::string Toast::GetIcon() - { - char ourPath[MAX_PATH] = { 0 }; - std::string file(ourPath, GetModuleFileNameA(GetModuleHandle(nullptr), ourPath, sizeof(ourPath))); - - auto pos = file.find_last_of("/\\"); - if (pos != std::string::npos) file = file.substr(0, pos); - - file.append("\\iw4x\\images\\icon.png"); - Utils::String::Replace(file, "/", "\\"); - return file; + std::lock_guard _(Mutex); + Queue.push({material, Utils::String::ToUpper(title), description, length, 0, callback}); } void Toast::Draw(UIToast* toast) @@ -51,9 +37,14 @@ namespace Components float fontSize = 0.9f; float descSize = 0.9f; - Game::Material* white = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "white").material; if (!white) return; - Game::Font_s* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; if (!font) return; - Game::Font_s* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; if (!descfont) return; + Game::Font_s* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; + Game::Font_s* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; + + if (font == nullptr || descfont == nullptr) + { + return; + } + Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f }; Game::vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.8f }; Game::vec4_t borderColor = { 1.0f, 1.0f, 1.0f, 0.2f }; @@ -64,7 +55,7 @@ namespace Components if (Game::Sys_Milliseconds() < startTime || (startTime + duration) < Game::Sys_Milliseconds()) return; // Fadein stuff - else if (Game::Sys_Milliseconds() - startTime < slideTime) + if (Game::Sys_Milliseconds() - startTime < slideTime) { int diffH = Renderer::Height() / 5; int diff = Game::Sys_Milliseconds() - startTime; @@ -88,8 +79,8 @@ namespace Components // Calculate width data int iOffset = (bHeight - imgDim) / 2; int iOffsetLeft = iOffset * 2; - float titleSize = Game::R_TextWidth(toast->title.data(), 0x7FFFFFFF, font) * fontSize; - float descrSize = Game::R_TextWidth(toast->desc.data(), 0x7FFFFFFF, descfont) * descSize; + float titleSize = Game::R_TextWidth(toast->title.data(), std::numeric_limits::max(), font) * fontSize; + float descrSize = Game::R_TextWidth(toast->desc.data(), std::numeric_limits::max(), descfont) * descSize; float bWidth = iOffsetLeft * 3 + imgDim + std::max(titleSize, descrSize); // Make stuff divisible by 2 @@ -99,33 +90,40 @@ namespace Components bHeight += (bHeight % 2); // Background - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, white); + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, *Game::whiteMaterial); // Border - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 - border), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Left - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + bWidth), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Right - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Top - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 - border), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Left + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + bWidth), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Right + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Top + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, *Game::whiteMaterial); // Bottom // Image Game::Material* image = toast->image; - if (!Materials::IsValid(image)) image = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material; + if (!Materials::IsValid(image)) + { + image = Game::DB_FindXAssetDefaultHeaderInternal(Game::XAssetType::ASSET_TYPE_MATERIAL).material; + } + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image); // Text float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim; float rightText = width / 2 + bWidth / 2 - cornerSize - iOffsetLeft; - Game::R_AddCmdDrawText(toast->title.data(), 0x7FFFFFFF, font, static_cast(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title - Game::R_AddCmdDrawText(toast->desc.data(), 0x7FFFFFFF, descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description + Game::R_AddCmdDrawText(toast->title.data(), std::numeric_limits::max(), font, static_cast(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title + Game::R_AddCmdDrawText(toast->desc.data(), std::numeric_limits::max(), descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description } void Toast::Handler() { - if (Toast::Queue.empty()) return; + if (Queue.empty()) + { + return; + } - std::lock_guard _(Toast::Mutex); + std::lock_guard _(Mutex); - Toast::UIToast* toast = &Toast::Queue.front(); + UIToast* toast = &Queue.front(); // Set start time if (!toast->start) @@ -136,30 +134,36 @@ namespace Components if ((toast->start + toast->length) < Game::Sys_Milliseconds()) { if (toast->callback) toast->callback(); - Toast::Queue.pop(); + Queue.pop(); } else { - Toast::Draw(toast); + Draw(toast); } } + + void Toast::CL_DrawScreen_Stub(int localClientNum) + { + Utils::Hook::Call(0x5AC950)(localClientNum); + + Handler(); + } + Toast::Toast() { - if (Dedicated::IsEnabled() || Monitor::IsEnabled()) return; + if (Dedicated::IsEnabled() || Monitor::IsEnabled() || ZoneBuilder::IsEnabled()) + { + return; + } - Scheduler::OnGameInitialized(Toast::Handler, Scheduler::Pipeline::RENDERER); + Utils::Hook(0x5ACB99, CL_DrawScreen_Stub, HOOK_CALL).install()->quick(); -#ifdef _DEBUG +#ifdef TEST_TOAST Command::Add("testtoast", []([[maybe_unused]] Command::Params* params) { - Toast::Show("cardicon_prestige10", "Test", "This is a test toast", 3000); + Show("cardicon_prestige10", "Test", "This is a test toast", 3000); }); #endif } - - Toast::~Toast() - { - Toast::Queue = std::queue(); - } } diff --git a/src/Components/Modules/Toast.hpp b/src/Components/Modules/Toast.hpp index 262b3370..f0d805e8 100644 --- a/src/Components/Modules/Toast.hpp +++ b/src/Components/Modules/Toast.hpp @@ -6,12 +6,8 @@ namespace Components { public: Toast(); - ~Toast(); - - static void Show(const std::string& image, const std::string& title, const std::string& description, int length, Utils::Slot callback = Utils::Slot()); - static void Show(Game::Material* material, const std::string& title, const std::string& description, int length, Utils::Slot callback = Utils::Slot()); - - static std::string GetIcon(); + static void Show(const std::string& image, const std::string& title, const std::string& description, int length, const Utils::Slot& callback = Utils::Slot()); + static void Show(Game::Material* material, const std::string& title, const std::string& description, int length, const Utils::Slot& callback = Utils::Slot()); private: class UIToast @@ -27,6 +23,7 @@ namespace Components static void Handler(); static void Draw(UIToast* toast); + static void CL_DrawScreen_Stub(int localClientNum); static std::queue Queue; static std::mutex Mutex; From ab0ee60563672bb963eb823e7a4f2e31b556fae3 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Mon, 27 Jun 2022 13:09:10 +0200 Subject: [PATCH 16/77] [Network] Disable unused oob handlers --- src/Components/Modules/Network.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index b30cc5c6..f434dc19 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -366,6 +366,11 @@ namespace Components // Handle client packets Utils::Hook(0x5AA703, Network::CL_HandleCommandStub, HOOK_JUMP).install()->quick(); + // Disable unused OOB packets handlers just to be sure + Utils::Hook::Set(0x5AA5B6, 0xEB); // CL_SteamServerAuth + Utils::Hook::Set(0x5AA69F, 0xEB); // echo + Utils::Hook::Set(0x5AAA82, 0xEB); // SP + Network::OnPacket("resolveAddress", [](const Address& address, [[maybe_unused]] const std::string& data) { Network::SendRaw(address, address.getString()); From b718803ecb44c5cab653fed0c36b3f2430bf5980 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Tue, 28 Jun 2022 09:26:43 +0200 Subject: [PATCH 17/77] [Bans] Refactor --- src/Components/Modules/Bans.cpp | 231 ++++++++++++++++------------ src/Components/Modules/Bans.hpp | 16 +- src/Components/Modules/Download.cpp | 4 +- src/Components/Modules/Download.hpp | 3 +- src/STDInclude.hpp | 1 + src/Utils/NamedMutex.cpp | 43 ++++++ src/Utils/NamedMutex.hpp | 24 +++ 7 files changed, 209 insertions(+), 113 deletions(-) create mode 100644 src/Utils/NamedMutex.cpp create mode 100644 src/Utils/NamedMutex.hpp diff --git a/src/Components/Modules/Bans.cpp b/src/Components/Modules/Bans.cpp index cf13ed16..45382bdc 100644 --- a/src/Components/Modules/Bans.cpp +++ b/src/Components/Modules/Bans.cpp @@ -2,14 +2,18 @@ namespace Components { - std::recursive_mutex Bans::AccessMutex; - - bool Bans::IsBanned(Bans::Entry entry) + // Have only one instance of IW4x read/write the file + std::unique_lock Bans::Lock() { - std::lock_guard _(Bans::AccessMutex); + static Utils::NamedMutex mutex{"iw4x-ban-list-lock"}; + std::unique_lock lock{mutex}; + return lock; + } - Bans::BanList list; - Bans::LoadBans(&list); + bool Bans::IsBanned(const banEntry& entry) + { + BanList list; + LoadBans(&list); if (entry.first.bits) { @@ -24,7 +28,7 @@ namespace Components if (entry.second.full) { - for (auto& ipEntry : list.ipList) + for (const auto& ipEntry : list.ipList) { if (ipEntry.full == entry.second.full) { @@ -36,17 +40,15 @@ namespace Components return false; } - void Bans::InsertBan(Bans::Entry entry) + void Bans::InsertBan(const banEntry& entry) { - std::lock_guard _(Bans::AccessMutex); - - Bans::BanList list; - Bans::LoadBans(&list); + BanList list; + LoadBans(&list); if (entry.first.bits) { bool found = false; - for (auto& idEntry : list.idList) + for (const auto& idEntry : list.idList) { if (idEntry.bits == entry.first.bits) { @@ -64,7 +66,7 @@ namespace Components if (entry.second.full) { bool found = false; - for (auto& ipEntry : list.ipList) + for (const auto& ipEntry : list.ipList) { if (ipEntry.full == entry.second.full) { @@ -79,12 +81,14 @@ namespace Components } } - Bans::SaveBans(&list); + SaveBans(&list); } - void Bans::SaveBans(BanList* list) + void Bans::SaveBans(const BanList* list) { - std::lock_guard _(Bans::AccessMutex); + assert(list != nullptr); + + const auto _ = Lock(); std::vector idVector; std::vector ipVector; @@ -109,95 +113,85 @@ namespace Components { "id", idVector }, }; - FileSystem::FileWriter ban("bans.json"); - ban.write(bans.dump()); + FileSystem::FileWriter ("bans.json").write(bans.dump()); } - void Bans::LoadBans(Bans::BanList* list) + void Bans::LoadBans(BanList* list) { - std::lock_guard _(Bans::AccessMutex); + assert(list != nullptr); + + const auto _ = Lock(); FileSystem::File bans("bans.json"); - if (bans.exists()) + if (!bans.exists()) { - std::string error; - json11::Json banData = json11::Json::parse(bans.getBuffer(), error); + Logger::Debug("bans.json does not exist"); + return; + } - if (!error.empty()) + std::string error; + const auto banData = json11::Json::parse(bans.getBuffer(), error); + + if (!error.empty()) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to parse bans.json: {}\n", error); + return; + } + + if (!banData.is_object()) + { + Logger::Debug("bans.json contains invalid data"); + return; + } + + const auto& idList = banData["id"]; + const auto& ipList = banData["ip"]; + + if (idList.is_array()) + { + for (auto &idEntry : idList.array_items()) { - Logger::Error(Game::ERR_FATAL, "Failed to parse bans (bans.json): {}", error); - } - - if (!list) return; - - if (banData.is_object()) - { - auto idList = banData["id"]; - auto ipList = banData["ip"]; - - if (idList.is_array()) + if (idEntry.is_string()) { - for (auto &idEntry : idList.array_items()) - { - if (idEntry.is_string()) - { - SteamID id; - id.bits = strtoull(idEntry.string_value().data(), nullptr, 16); + SteamID id; + id.bits = strtoull(idEntry.string_value().data(), nullptr, 16); - list->idList.push_back(id); - } - } + list->idList.push_back(id); } + } + } - if (ipList.is_array()) + if (ipList.is_array()) + { + for (auto &ipEntry : ipList.array_items()) + { + if (ipEntry.is_string()) { - for (auto &ipEntry : ipList.array_items()) - { - if (ipEntry.is_string()) - { - Network::Address addr(ipEntry.string_value()); + Network::Address addr(ipEntry.string_value()); - list->ipList.push_back(addr.getIP()); - } - } + list->ipList.push_back(addr.getIP()); } } } } - void Bans::BanClientNum(int num, const std::string& reason) + void Bans::BanClient(Game::client_t* cl, const std::string& reason) { - if (!Dvar::Var("sv_running").get()) - { - Logger::Print("Server is not running.\n"); - return; - } - - if (*Game::svs_clientCount <= num) - { - Logger::Print("Player {} is not on the server\n", num); - return; - } - - Game::client_t* client = &Game::svs_clients[num]; - SteamID guid; - guid.bits = client->steamID; + guid.bits = cl->steamID; - Bans::InsertBan({guid, client->netchan.remoteAddress.ip}); + InsertBan({guid, cl->netchan.remoteAddress.ip}); - Game::SV_GameDropClient(num, reason.data()); + Game::SV_DropClient(cl, reason.data(), true); } void Bans::UnbanClient(SteamID id) { - std::lock_guard _(Bans::AccessMutex); + BanList list; + LoadBans(&list); - Bans::BanList list; - Bans::LoadBans(&list); - - auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](SteamID& entry) + const auto entry = std::find_if(list.idList.begin(), list.idList.end(), [&id](const SteamID& entry) { return id.bits == entry.bits; }); @@ -207,17 +201,15 @@ namespace Components list.idList.erase(entry); } - Bans::SaveBans(&list); + SaveBans(&list); } void Bans::UnbanClient(Game::netIP_t ip) { - std::lock_guard _(Bans::AccessMutex); + BanList list; + LoadBans(&list); - Bans::BanList list; - Bans::LoadBans(&list); - - auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](Game::netIP_t& entry) + const auto entry = std::find_if(list.ipList.begin(), list.ipList.end(), [&ip](const Game::netIP_t& entry) { return ip.full == entry.full; }); @@ -227,31 +219,75 @@ namespace Components list.ipList.erase(entry); } - Bans::SaveBans(&list); + SaveBans(&list); } Bans::Bans() { - Command::Add("banclient", [](Command::Params* params) + Command::Add("banClient", [](Command::Params* params) { - if (params->size() < 2) return; + if (!Dvar::Var("sv_running").get()) + { + Logger::Print("Server is not running.\n"); + return; + } - std::string reason = "EXE_ERR_BANNED_PERM"; - if (params->size() >= 3) reason = params->join(2); + if (params->size() < 2) + { + Logger::Print("{} : permanently ban a client\n", params->get(0)); + return; + } - Bans::BanClientNum(atoi(params->get(1)), reason); + const auto* input = params->get(1); + + for (auto i = 0; input[i] != '\0'; ++i) + { + if (input[i] < '0' || input[i] > '9') + { + Logger::Print("Bad slot number: {}\n", input); + return; + } + } + + const auto num = std::atoi(input); + + if (num < 0 || num >= *Game::svs_clientCount) + { + Logger::Print("Bad client slot: {}\n", num); + return; + } + + const auto* cl = &Game::svs_clients[num]; + if (cl->state == Game::CS_FREE) + { + Logger::Print("Client {} is not active\n", num); + return; + } + + const std::string reason = params->size() < 3 ? "EXE_ERR_BANNED_PERM" : params->join(2); + Bans::BanClient(&Game::svs_clients[num], reason); }); - Command::Add("unbanclient", [](Command::Params* params) + Command::Add("unbanClient", [](Command::Params* params) { - if (params->size() < 2) return; + if (!Dvar::Var("sv_running").get()) + { + Logger::Print("Server is not running.\n"); + return; + } - std::string type = params->get(1); + if (params->size() < 3) + { + Logger::Print("{} \n", params->get(0)); + return; + } + + const auto* type = params->get(1); if (type == "ip"s) { Network::Address address(params->get(2)); - Bans::UnbanClient(address.getIP()); + UnbanClient(address.getIP()); Logger::Print("Unbanned IP {}\n", params->get(2)); @@ -261,17 +297,10 @@ namespace Components SteamID id; id.bits = strtoull(params->get(2), nullptr, 16); - Bans::UnbanClient(id); + UnbanClient(id); Logger::Print("Unbanned GUID {}\n", params->get(2)); } }); - - // Verify the list on startup - Scheduler::OnGameInitialized([] - { - Bans::BanList list; - Bans::LoadBans(&list); - }, Scheduler::Pipeline::SERVER); } } diff --git a/src/Components/Modules/Bans.hpp b/src/Components/Modules/Bans.hpp index fe5c6410..0ba36d12 100644 --- a/src/Components/Modules/Bans.hpp +++ b/src/Components/Modules/Bans.hpp @@ -5,27 +5,27 @@ namespace Components class Bans : public Component { public: - typedef std::pair Entry; + using banEntry = std::pair; Bans(); - static void BanClientNum(int num, const std::string& reason); + static std::unique_lock Lock(); + + static void BanClient(Game::client_t* cl, const std::string& reason); static void UnbanClient(SteamID id); static void UnbanClient(Game::netIP_t ip); - static bool IsBanned(Entry entry); - static void InsertBan(Entry entry); + static bool IsBanned(const banEntry& entry); + static void InsertBan(const banEntry& entry); private: - class BanList + struct BanList { - public: std::vector idList; std::vector ipList; }; - static std::recursive_mutex AccessMutex; static void LoadBans(BanList* list); - static void SaveBans(BanList* list); + static void SaveBans(const BanList* list); }; } diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index d16e76ee..6cda7c4c 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -8,7 +8,7 @@ namespace Components std::thread Download::ServerThread; bool Download::Terminate; - bool Download::ServerRunning; + bool Download::ServerRunning; #pragma region Client @@ -889,7 +889,7 @@ namespace Components } }); - Download::ServerRunning = true; + Download::ServerRunning = true; Download::Terminate = false; Download::ServerThread = std::thread([] { diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index 90472069..cf934f34 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -1,5 +1,4 @@ #pragma once -#include namespace Components { @@ -210,7 +209,7 @@ namespace Components static std::vector> ScriptDownloads; static std::thread ServerThread; static bool Terminate; - static bool ServerRunning; + static bool ServerRunning; static void DownloadProgress(FileDownload* fDownload, size_t bytes); diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 950c5248..d2deaa11 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -129,6 +129,7 @@ using namespace std::literals; #include "Utils/Json.hpp" #include "Utils/Library.hpp" #include "Utils/Maths.hpp" +#include "Utils/NamedMutex.hpp" #include "Utils/String.hpp" #include "Utils/Thread.hpp" #include "Utils/Time.hpp" diff --git a/src/Utils/NamedMutex.cpp b/src/Utils/NamedMutex.cpp new file mode 100644 index 00000000..48bfed99 --- /dev/null +++ b/src/Utils/NamedMutex.cpp @@ -0,0 +1,43 @@ +#include + +namespace Utils +{ + NamedMutex::NamedMutex(const std::string& name) + { + this->handle_ = CreateMutexA(nullptr, FALSE, name.data()); + } + + NamedMutex::~NamedMutex() + { + if (this->handle_) + { + CloseHandle(this->handle_); + } + } + + void NamedMutex::lock() const + { + if (this->handle_) + { + WaitForSingleObject(this->handle_, INFINITE); + } + } + + bool NamedMutex::try_lock(const std::chrono::milliseconds timeout) const + { + if (this->handle_) + { + return WAIT_OBJECT_0 == WaitForSingleObject(this->handle_, static_cast(timeout.count())); + } + + return false; + } + + void NamedMutex::unlock() const noexcept + { + if (this->handle_) + { + ReleaseMutex(this->handle_); + } + } +} diff --git a/src/Utils/NamedMutex.hpp b/src/Utils/NamedMutex.hpp new file mode 100644 index 00000000..15bf892d --- /dev/null +++ b/src/Utils/NamedMutex.hpp @@ -0,0 +1,24 @@ +#pragma once + +namespace Utils +{ + class NamedMutex + { + public: + explicit NamedMutex(const std::string& name); + ~NamedMutex(); + + NamedMutex(NamedMutex&&) = delete; + NamedMutex(const NamedMutex&) = delete; + NamedMutex& operator=(NamedMutex&&) = delete; + NamedMutex& operator=(const NamedMutex&) = delete; + + void lock() const; + // Lockable requirements + [[nodiscard]] bool try_lock(std::chrono::milliseconds timeout = std::chrono::milliseconds{0}) const; + void unlock() const noexcept; + + private: + void* handle_{}; + }; +} From 8f9145d1e08894bf4125eef9405f3f5dceab1963 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 28 Jun 2022 19:28:52 +0200 Subject: [PATCH 18/77] Bump deps/zlib from `ec3df00` to `2333419` (#338) Bumps [deps/zlib](https://github.com/madler/zlib) from `ec3df00` to `2333419`. - [Release notes](https://github.com/madler/zlib/releases) - [Commits](https://github.com/madler/zlib/compare/ec3df00224d4b396e2ac6586ab5d25f673caa4c2...2333419cd76cb9ae5f15c9b240b16a2052b27691) --- updated-dependencies: - dependency-name: deps/zlib dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- deps/zlib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/zlib b/deps/zlib index ec3df002..2333419c 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit ec3df00224d4b396e2ac6586ab5d25f673caa4c2 +Subproject commit 2333419cd76cb9ae5f15c9b240b16a2052b27691 From c467e1d5382830c3d986513ce1c73ff076c09aea Mon Sep 17 00:00:00 2001 From: Diavolo Date: Tue, 28 Jun 2022 20:57:58 +0200 Subject: [PATCH 19/77] [Command] Have SV commands register sooner --- src/Components/Modules/Chat.cpp | 2 +- src/Components/Modules/Command.cpp | 5 +---- src/Components/Modules/Dedicated.cpp | 2 +- src/Components/Modules/Events.cpp | 16 ++++++++++++++++ src/Components/Modules/Events.hpp | 5 +++++ src/Components/Modules/Logger.cpp | 2 +- 6 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index d40e2082..beb95f0b 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -410,7 +410,7 @@ namespace Components { cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, std::numeric_limits::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); sv_disableChat = Dvar::Register("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); - Scheduler::OnGameInitialized(AddChatCommands, Scheduler::Pipeline::SERVER); + Events::OnSVInit(AddChatCommands); // Intercept chat sending Utils::Hook(0x4D000B, PreSayStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Command.cpp b/src/Components/Modules/Command.cpp index 7e9f58dd..0df8ec9c 100644 --- a/src/Components/Modules/Command.cpp +++ b/src/Components/Modules/Command.cpp @@ -98,9 +98,6 @@ namespace Components if (!Command::FunctionMapSV.contains(command)) { Command::AddRawSV(name, Command::MainCallbackSV); - - // If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler - Command::AddRaw(name, Game::Cbuf_AddServerText); } FunctionMapSV.insert_or_assign(command, callback); @@ -116,7 +113,7 @@ namespace Components Game::Cmd_AddServerCommand(name, callback, Command::Allocate()); // If the main command is registered as Cbuf_AddServerText, the command will be redirected to the SV handler - Command::AddRaw(name, Game::Cbuf_AddServerText); + Command::AddRaw(name, Game::Cbuf_AddServerText, false); } void Command::Execute(std::string command, bool sync) diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 7d68e749..22f49aef 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -285,7 +285,7 @@ namespace Components Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_NONE, "A custom message of the day for servers"); }, Scheduler::Pipeline::MAIN); - Scheduler::OnGameInitialized(Dedicated::AddDedicatedCommands, Scheduler::Pipeline::SERVER); + Events::OnSVInit(Dedicated::AddDedicatedCommands); // Post initialization point Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/Events.cpp b/src/Components/Modules/Events.cpp index 5a940f2a..b73ef3a1 100644 --- a/src/Components/Modules/Events.cpp +++ b/src/Components/Modules/Events.cpp @@ -5,6 +5,7 @@ namespace Components Utils::Signal Events::ClientDisconnectSignal; Utils::Signal Events::SteamDisconnectSignal; Utils::Signal Events::ShutdownSystemSignal; + Utils::Signal Events::ServerInitSignal; void Events::OnClientDisconnect(const Utils::Slot& callback) { @@ -21,6 +22,11 @@ namespace Components ShutdownSystemSignal.connect(callback); } + void Events::OnSVInit(const Utils::Slot& callback) + { + ServerInitSignal.connect(callback); + } + /* * Should be called when a client drops from the server * but not "between levels" (Quake-III-Arena) @@ -46,6 +52,14 @@ namespace Components Utils::Hook::Call(0x421EE0)(sys); // Scr_ShutdownSystem } + void Events::SV_Init_Hk() + { + ServerInitSignal(); + ServerInitSignal.clear(); + + Utils::Hook::Call(0x474320)(); // SV_InitGameMode + } + Events::Events() { Utils::Hook(0x625235, ClientDisconnect_Hk, HOOK_CALL).install()->quick(); // SV_FreeClient @@ -54,5 +68,7 @@ namespace Components Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame Utils::Hook(0x4D06BA, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_ShutdownGame + + Utils::Hook(0x4D3665, SV_Init_Hk, HOOK_CALL).install()->quick(); // SV_Init } } diff --git a/src/Components/Modules/Events.hpp b/src/Components/Modules/Events.hpp index 8da45250..08a3675a 100644 --- a/src/Components/Modules/Events.hpp +++ b/src/Components/Modules/Events.hpp @@ -18,13 +18,18 @@ namespace Components static void OnVMShutdown(const Utils::Slot& callback); + // Client & Server (triggered once) + static void OnSVInit(const Utils::Slot& callback); + private: static Utils::Signal ClientDisconnectSignal; static Utils::Signal SteamDisconnectSignal; static Utils::Signal ShutdownSystemSignal; + static Utils::Signal ServerInitSignal; static void ClientDisconnect_Hk(int clientNum); static void SteamDisconnect_Hk(); static void Scr_ShutdownSystem_Hk(unsigned char sys); + static void SV_Init_Hk(); }; } diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 34c9c0b4..9d9ecf89 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -379,7 +379,7 @@ namespace Components Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).install()->quick(); } - Scheduler::OnGameInitialized(Logger::AddServerCommands, Scheduler::Pipeline::SERVER); + Events::OnSVInit(Logger::AddServerCommands); } Logger::~Logger() From dbef5f7a1640ea5eaac18b076b4e02a3a5ab702f Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 12:05:56 +0200 Subject: [PATCH 20/77] [Scheduler] Use old hook for renderer pipeline --- src/Components/Modules/Scheduler.cpp | 8 +++----- src/Components/Modules/Scheduler.hpp | 2 +- src/Components/Modules/Toast.cpp | 10 +--------- src/Components/Modules/Toast.hpp | 1 - 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/Components/Modules/Scheduler.cpp b/src/Components/Modules/Scheduler.cpp index 858970a4..94a778f4 100644 --- a/src/Components/Modules/Scheduler.cpp +++ b/src/Components/Modules/Scheduler.cpp @@ -67,9 +67,9 @@ namespace Components Pipelines[type].execute(); } - void Scheduler::REndFrame_Hk() + void Scheduler::CL_DrawScreen_Hk() { - Utils::Hook::Call(0x50AB20)(); + Utils::Hook::Call(0x5AC950)(); Execute(Pipeline::RENDERER); } @@ -165,9 +165,7 @@ namespace Components } }); - Utils::Hook(0x4DBE9A, REndFrame_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x518D5C, REndFrame_Hk, HOOK_CALL).install()->quick(); - Utils::Hook(0x5ACBA3, REndFrame_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x5ACB99, CL_DrawScreen_Hk, HOOK_CALL).install()->quick(); // Hook G_Glass_Update so we may fix TLS issues Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Scheduler.hpp b/src/Components/Modules/Scheduler.hpp index 5d430a9b..c445e4ad 100644 --- a/src/Components/Modules/Scheduler.hpp +++ b/src/Components/Modules/Scheduler.hpp @@ -59,7 +59,7 @@ namespace Components static void Execute(Pipeline type); - static void REndFrame_Hk(); + static void CL_DrawScreen_Hk(); static void ServerFrame_Hk(); static void ClientFrame_Hk(int localClientNum); static void MainFrame_Hk(); diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index 58f07398..4014f043 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -142,14 +142,6 @@ namespace Components } } - - void Toast::CL_DrawScreen_Stub(int localClientNum) - { - Utils::Hook::Call(0x5AC950)(localClientNum); - - Handler(); - } - Toast::Toast() { if (Dedicated::IsEnabled() || Monitor::IsEnabled() || ZoneBuilder::IsEnabled()) @@ -157,7 +149,7 @@ namespace Components return; } - Utils::Hook(0x5ACB99, CL_DrawScreen_Stub, HOOK_CALL).install()->quick(); + Scheduler::Loop(Handler, Scheduler::Pipeline::RENDERER); #ifdef TEST_TOAST Command::Add("testtoast", []([[maybe_unused]] Command::Params* params) diff --git a/src/Components/Modules/Toast.hpp b/src/Components/Modules/Toast.hpp index f0d805e8..6045d442 100644 --- a/src/Components/Modules/Toast.hpp +++ b/src/Components/Modules/Toast.hpp @@ -23,7 +23,6 @@ namespace Components static void Handler(); static void Draw(UIToast* toast); - static void CL_DrawScreen_Stub(int localClientNum); static std::queue Queue; static std::mutex Mutex; From 16988b24f4b210f36810df89b602de2f83774241 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 13:32:19 +0200 Subject: [PATCH 21/77] [Renderer] Fix crash when debugging oilrig --- src/Components/Modules/Renderer.cpp | 71 ++++++++++++++++------------- src/Game/Functions.hpp | 4 +- 2 files changed, 42 insertions(+), 33 deletions(-) diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index dd18f02a..4c369e1e 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -177,7 +177,7 @@ namespace Components auto entities = Game::g_entities; - for (auto i = 0u; i < 0x800u; i++) + for (std::size_t i = 0; i < Game::MAX_GENTITIES; ++i) { auto* ent = &entities[i]; @@ -229,7 +229,7 @@ namespace Components auto scene = Game::scene; - for (auto i = 0; i < scene->sceneModelCount; i++) + for (auto i = 0; i < scene->sceneModelCount; ++i) { if (!scene->sceneModel[i].model) continue; @@ -255,23 +255,27 @@ namespace Components if (!val) return; - // Ingame only - int clientNum = Game::CG_GetClientNum(); - if (!Game::CL_IsCgameInitialized() || - clientNum >= 18 || - clientNum < 0 || - Game::g_entities[clientNum].client == nullptr) { - + auto clientNum = Game::CG_GetClientNum(); + Game::gentity_t* clientEntity = &Game::g_entities[clientNum]; + + // Ingame only & player only + if (!Game::CL_IsCgameInitialized() || clientEntity->client == nullptr) + { return; } - Game::gentity_t* clientEntity = &Game::g_entities[clientNum]; - float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] }; const auto mapName = Dvar::Var("mapname").get(); auto scene = Game::scene; - auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld; + auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName)); + + if (gfxAsset == nullptr) + { + return; + } + + auto world = gfxAsset->asset.header.gfxWorld; auto drawDistance = r_playerDrawDebugDistance.get(); auto sqrDist = drawDistance * drawDistance; @@ -297,7 +301,6 @@ namespace Components } } break; - case 2: for (auto i = 0; i < scene->sceneDObjCount; i++) { @@ -320,7 +323,6 @@ namespace Components } } break; - case 3: // Static models for (size_t i = 0; i < world->dpvs.smodelCount; i++) @@ -344,6 +346,8 @@ namespace Components } } break; + default: + break; } } @@ -353,41 +357,45 @@ namespace Components if (!val) return; - // Ingame only - int clientNum = Game::CG_GetClientNum(); - if (!Game::CL_IsCgameInitialized() || - clientNum >= 18 || - clientNum < 0 || - Game::g_entities[clientNum].client == nullptr) { + auto clientNum = Game::CG_GetClientNum(); + Game::gentity_t* clientEntity = &Game::g_entities[clientNum]; + // Ingame only & player only + if (!Game::CL_IsCgameInitialized() || clientEntity->client == nullptr) + { return; } - Game::gentity_t* clientEntity = &Game::g_entities[clientNum]; - float playerPosition[3]{ clientEntity->r.currentOrigin[0], clientEntity->r.currentOrigin[1], clientEntity->r.currentOrigin[2] }; const auto mapName = Dvar::Var("mapname").get(); auto scene = Game::scene; - auto world = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName))->asset.header.gfxWorld; + auto gfxAsset = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_GFXWORLD, Utils::String::VA("maps/mp/%s.d3dbsp", mapName)); + + if (gfxAsset == nullptr) + { + return; + } + + auto world = gfxAsset->asset.header.gfxWorld; auto drawDistance = r_playerDrawDebugDistance.get(); auto sqrDist = drawDistance * drawDistance; - switch (val) { + switch (val) + { case 1: for (auto i = 0; i < scene->sceneModelCount; i++) { if (!scene->sceneModel[i].model) continue; - if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneModel[i].placement.base.origin) < static_cast(sqrDist)) { Game::R_AddDebugString(sceneModelsColor, scene->sceneModel[i].placement.base.origin, 1.0, scene->sceneModel[i].model->name); } } break; - case 2: for (auto i = 0; i < scene->sceneDObjCount; i++) { @@ -395,7 +403,7 @@ namespace Components { for (int j = 0; j < scene->sceneDObj[i].obj->numModels; j++) { - if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < sqrDist) + if (Utils::Maths::Vec3SqrDistance(playerPosition, scene->sceneDObj[i].placement.origin) < static_cast(sqrDist)) { Game::R_AddDebugString(dobjsColor, scene->sceneDObj[i].placement.origin, 1.0, scene->sceneDObj[i].obj->models[j]->name); } @@ -403,7 +411,6 @@ namespace Components } } break; - case 3: // Static models for (size_t i = 0; i < world->dpvs.smodelCount; i++) @@ -412,13 +419,15 @@ namespace Components if (staticModel.model) { const auto dist = Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin); - if (dist < sqrDist) + if (dist < static_cast(sqrDist)) { Game::R_AddDebugString(staticModelsColor, staticModel.placement.origin, 1.0, staticModel.model->name); } } } break; + default: + break; } } @@ -467,14 +476,14 @@ namespace Components Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick(); // Begin device recovery (not D3D9Ex) - Utils::Hook(0x508298, []() + Utils::Hook(0x508298, [] { Game::DB_BeginRecoverLostDevice(); Renderer::BeginRecoverDeviceSignal(); }, HOOK_CALL).install()->quick(); // End device recovery (not D3D9Ex) - Utils::Hook(0x508355, []() + Utils::Hook(0x508355, [] { Renderer::EndRecoverDeviceSignal(); Game::DB_EndRecoverLostDevice(); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index f043bc0d..0d14d299 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1143,8 +1143,8 @@ namespace Game extern int* demoRecording; extern int* serverMessageSequence; - constexpr auto MAX_GENTITIES = 2048u; - constexpr auto ENTITYNUM_NONE = MAX_GENTITIES - 1; + constexpr std::size_t MAX_GENTITIES = 2048; + constexpr std::size_t ENTITYNUM_NONE = MAX_GENTITIES - 1; extern gentity_t* g_entities; extern netadr_t* connectedHost; From 1e67fadc42ab5cf5fde8030d15e01bd9e5692d89 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 17:07:39 +0200 Subject: [PATCH 22/77] [Chat] Isolate stack --- src/Components/Modules/Chat.cpp | 1 + src/Game/Scripting/StackIsolation.cpp | 26 ++++++++++++++++++++++++++ src/Game/Scripting/StackIsolation.hpp | 24 ++++++++++++++++++++++++ src/STDInclude.hpp | 1 + 4 files changed, 52 insertions(+) create mode 100644 src/Game/Scripting/StackIsolation.cpp create mode 100644 src/Game/Scripting/StackIsolation.hpp diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index beb95f0b..ead9e87b 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -369,6 +369,7 @@ namespace Components { const auto entityId = Game::Scr_GetEntityId(self->s.number, 0); + Scripting::StackIsolation _; Game::Scr_AddInt(mode); Game::Scr_AddString(message); diff --git a/src/Game/Scripting/StackIsolation.cpp b/src/Game/Scripting/StackIsolation.cpp new file mode 100644 index 00000000..feed4e07 --- /dev/null +++ b/src/Game/Scripting/StackIsolation.cpp @@ -0,0 +1,26 @@ +#include + +namespace Scripting +{ + StackIsolation::StackIsolation() + { + this->inParamCount_ = Game::scrVmPub->inparamcount; + this->outParamCount_ = Game::scrVmPub->outparamcount; + this->top_ = Game::scrVmPub->top; + this->maxStack_ = Game::scrVmPub->maxStack; + + Game::scrVmPub->top = this->stack_; + Game::scrVmPub->maxStack = &this->stack_[ARRAYSIZE(this->stack_) - 1]; + Game::scrVmPub->inparamcount = 0; + Game::scrVmPub->outparamcount = 0; + } + + StackIsolation::~StackIsolation() + { + Game::Scr_ClearOutParams(); + Game::scrVmPub->inparamcount = this->inParamCount_; + Game::scrVmPub->outparamcount = this->outParamCount_; + Game::scrVmPub->top = this->top_; + Game::scrVmPub->maxStack = this->maxStack_; + } +} diff --git a/src/Game/Scripting/StackIsolation.hpp b/src/Game/Scripting/StackIsolation.hpp new file mode 100644 index 00000000..dfe6bd4a --- /dev/null +++ b/src/Game/Scripting/StackIsolation.hpp @@ -0,0 +1,24 @@ +#pragma once + +namespace Scripting +{ + class StackIsolation final + { + public: + StackIsolation(); + ~StackIsolation(); + + StackIsolation(StackIsolation&&) = delete; + StackIsolation(const StackIsolation&) = delete; + StackIsolation& operator=(StackIsolation&&) = delete; + StackIsolation& operator=(const StackIsolation&) = delete; + + private: + Game::VariableValue stack_[512]{}; + + Game::VariableValue* maxStack_; + Game::VariableValue* top_; + unsigned int inParamCount_; + unsigned int outParamCount_; + }; +} diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index d2deaa11..4ea171d8 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -141,6 +141,7 @@ using namespace std::literals; #include "Game/Structs.hpp" #include "Game/Functions.hpp" #include +#include #include "Utils/Stream.hpp" // Breaks order on purpose From 46842a582c580db5216c8f392b803629e943cb1c Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 17:36:54 +0200 Subject: [PATCH 23/77] [Struct] Document clientConnections --- src/Game/Functions.cpp | 2 ++ src/Game/Functions.hpp | 2 ++ src/Game/Structs.hpp | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 33917332..121fdca5 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -605,6 +605,8 @@ namespace Game scr_const_t* scr_const = reinterpret_cast(0x1AA2E00); + clientConnection_t* clientConnections = reinterpret_cast(0xA1E878); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index f043bc0d..8ae03984 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1251,6 +1251,8 @@ namespace Game extern scr_const_t* scr_const; + extern clientConnection_t* clientConnections; + void Sys_LockRead(FastCriticalSection* critSect); void Sys_UnlockRead(FastCriticalSection* critSect); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index d066ca54..0eebd158 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -6264,6 +6264,49 @@ namespace Game static_assert(sizeof(client_t) == 0xA6790); + struct clientConnection_t + { + int qport; + int clientNum; + int lastPacketSentTime; + int lastPacketTime; + netadr_t serverAddress; + int connectTime; + int connectPacketCount; + char serverMessage[256]; + int challenge; + int checksumFeed; + int reliableSequence; + int reliableAcknowledge; + char reliableCommands[128][1024]; + int serverMessageSequence; + int serverCommandSequence; + int lastExecutedServerCommand; + char serverCommands[128][1024]; + bool isServerRestarting; + int lastClientArchiveIndex; + char demoName[64]; + int demorecording; + int demoplaying; + int isTimeDemo; + int demowaiting; + int(__cdecl* demoread)(void*, int, int); + int demofile; + int timeDemoLog; + int timeDemoFrames; + int timeDemoStart; + int timeDemoPrev; + int timeDemoBaseTime; + netchan_t netchan; + char netchanOutgoingBuffer[2048]; + char netchanIncomingBuffer[131072]; + netProfileInfo_t OOBProf; + char statPacketsToSend; + int statPacketSendTime[7]; + }; + + static_assert(sizeof(clientConnection_t) == 0x615E8); // Size confirmed in CL_Migrate + struct CModelAllocData { void* mainArray; From 88ec372616970faaa7be97ea999324fedcc1450f Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 19:42:29 +0200 Subject: [PATCH 24/77] [IMaterial] fix format string --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index d12c153b..4e321983 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -112,8 +112,9 @@ namespace Assets } } } - else { - Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with %s\n", techsetName, asset->techniqueSet->name); + else + { + Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with {}\n", techsetName, asset->techniqueSet->name); } if (!asset->techniqueSet) From 769ff3fd5eeafcc8715356b8b29b43df86f44752 Mon Sep 17 00:00:00 2001 From: Edo Date: Thu, 30 Jun 2022 21:37:47 +0200 Subject: [PATCH 25/77] Custom vision file parser (#249) --- src/Components/Loader.cpp | 5 +- src/Components/Loader.hpp | 1 + src/Components/Modules/VisionFile.cpp | 111 +++++++++++++++++++++++++ src/Components/Modules/VisionFile.hpp | 19 +++++ src/Components/Modules/ZoneBuilder.cpp | 18 ++-- src/Game/Functions.cpp | 51 +++++++++++- src/Game/Functions.hpp | 14 ++++ src/Game/Structs.hpp | 1 + 8 files changed, 207 insertions(+), 13 deletions(-) create mode 100644 src/Components/Modules/VisionFile.cpp create mode 100644 src/Components/Modules/VisionFile.hpp diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 43a4acb1..004f316e 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -31,8 +31,8 @@ namespace Components Loader::Register(new Flags()); Loader::Register(new Singleton()); - Loader::Register(new Exception()); // install our exception handler as early as posssible to get better debug dumps from startup crashes - + // Install our exception handler as early as posssible to get better debug dumps from startup crashes + Loader::Register(new Exception()); Loader::Register(new Auth()); Loader::Register(new Bans()); Loader::Register(new Bots()); @@ -104,6 +104,7 @@ namespace Components Loader::Register(new Movement()); Loader::Register(new Elevators()); Loader::Register(new ClientCommand()); + Loader::Register(new VisionFile()); Loader::Register(new ScriptExtension()); Loader::Register(new Branding()); Loader::Register(new Debug()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index cdfb31ea..f3eee146 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -134,6 +134,7 @@ namespace Components #include "Modules/Movement.hpp" #include "Modules/Elevators.hpp" #include "Modules/ClientCommand.hpp" +#include "Modules/VisionFile.hpp" #include "Modules/Gamepad.hpp" #include "Modules/ScriptExtension.hpp" #include "Modules/Branding.hpp" diff --git a/src/Components/Modules/VisionFile.cpp b/src/Components/Modules/VisionFile.cpp new file mode 100644 index 00000000..a030515b --- /dev/null +++ b/src/Components/Modules/VisionFile.cpp @@ -0,0 +1,111 @@ +#include + +namespace Components +{ + const char* VisionFile::DvarExceptions[] = + { + "r_pretess", + }; + + void VisionFile::ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName) + { + for (std::size_t i = 0; i < std::extent_v; ++i) + { + if (dvarName == DvarExceptions[i]) + { + const auto* dvar = Game::Dvar_FindVar(dvarName.data()); + + assert(dvar != nullptr); + + const auto* currentVal = Game::Dvar_DisplayableValue(dvar); + const auto* parsedValue = Game::Com_ParseOnLine(&buffer); + + if (std::strcmp(parsedValue, currentVal) == 0) + { + // The dvar is already set to the value we want + return; + } + + Game::Dvar_SetFromStringFromSource(dvar, parsedValue, Game::DvarSetSource::DVAR_SOURCE_INTERNAL); + Logger::Print("Overriding '{}' from '{}'\n", dvarName, fileName); + + // Successfully found and tried to apply the string value to the dvar + return; + } + } + + Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM, + "WARNING: unknown dvar \'%s\' in file \'%s\'\n", dvarName.data(), fileName.data()); + } + + // Gets the dvar name and value and attemps to apply it to the vision settings + void VisionFile::ApplyValueToSettings(const std::string& key, const char* buffer, + const std::string& fileName, Game::visionSetVars_t* settings) + { + for (std::size_t i = 0; i < 21; ++i) + { + // Must be case insensitive comparison + if (key == Utils::String::ToLower(Game::visionDefFields[i].name)) + { + auto* const dvarValue = Game::Com_ParseOnLine(&buffer); + + if (!Game::ApplyTokenToField(i, dvarValue, settings)) + { + Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM, + "WARNING: malformed dvar \'%s\' in file \'%s\'\n", dvarValue, fileName.data()); + + // Failed to apply the value. Check that sscanf can actually parse the value + return; + } + + // Successfully found and applied the value to the settings + return; + } + } + + // Dvar not found in visionDefFields, let's try to see if it's a 'patched' dvar + ApplyExemptDvar(key, buffer, fileName); + } + + bool VisionFile::LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings) + { + assert(settings != nullptr); + assert(fileName != nullptr); + + Game::Com_BeginParseSession(fileName); + + // Will split the buffer into tokens using the following delimiters: space, newlines (more?) + for (auto i = Game::Com_Parse(&buffer); *i != '\0'; i = Game::Com_Parse(&buffer)) + { + // Converting 'key' to lower case as it will be needed later + ApplyValueToSettings(Utils::String::ToLower(i), buffer, fileName, settings); + Game::Com_SkipRestOfLine(&buffer); + } + + Game::Com_EndParseSession(); + return true; + } + + __declspec(naked) bool VisionFile::LoadVisionSettingsFromBuffer_Stub() + { + // No need for push/pop ad guards, I have checked :) + __asm + { + push [esp + 0x8] // settings + push ebx // filename + push [esp + 0xC] // buffer + call VisionFile::LoadVisionSettingsFromBuffer + add esp, 0xC + + ret + } + } + + VisionFile::VisionFile() + { + AssertSize(Game::visField_t, 12); + + // Place hook in LoadVisionFile function + Utils::Hook(0x59A98A, LoadVisionSettingsFromBuffer_Stub, HOOK_CALL).install()->quick(); + } +} diff --git a/src/Components/Modules/VisionFile.hpp b/src/Components/Modules/VisionFile.hpp new file mode 100644 index 00000000..d9d379a9 --- /dev/null +++ b/src/Components/Modules/VisionFile.hpp @@ -0,0 +1,19 @@ +#pragma once + +namespace Components +{ + class VisionFile : public Component + { + public: + VisionFile(); + + private: + static const char* DvarExceptions[]; + + static void ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName); + static void ApplyValueToSettings(const std::string& dvarName, const char* buffer, const std::string& fileName, Game::visionSetVars_t* settings); + + static bool LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings); + static bool LoadVisionSettingsFromBuffer_Stub(); + }; +} diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index c07bcd8f..c966f885 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -95,7 +95,7 @@ namespace Components void ZoneBuilder::Zone::Zone::build() { - if(!this->dataMap.isValid()) + if (!this->dataMap.isValid()) { Logger::Print("Unable to load CSV for '{}'!\n", this->zoneName); return; @@ -111,7 +111,7 @@ namespace Components Logger::Print("Saving...\n"); this->saveData(); - if(this->buffer.hasBlock()) + if (this->buffer.hasBlock()) { Logger::Error(Game::ERR_FATAL, "Non-popped blocks left!\n"); } @@ -267,7 +267,7 @@ namespace Components const char* assetName = Game::DB_GetXAssetName(asset); if (assetName[0] == ',') ++assetName; - if(this->getAssetName(type, assetName) == name) + if (this->getAssetName(type, assetName) == name) { return i; } @@ -336,7 +336,7 @@ namespace Components if (assetIndex == -1) // nested asset { // already written. find alias and store in ptr - if(this->hasAlias(asset)) + if (this->hasAlias(asset)) { header.data = reinterpret_cast(this->getAlias(asset)); } @@ -1054,7 +1054,7 @@ namespace Components AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader, const std::string&, bool* restrict) { //if (*static_cast(Game::DB_XAssetPool[type].data) == 0) - if(Game::g_poolSize[type] == 0) + if (Game::g_poolSize[type] == 0) { *restrict = true; } @@ -1070,9 +1070,9 @@ namespace Components { int result = Utils::Hook::Call(0x642FC0)(dvar, value); - if(result) + if (result) { - if(std::string(value.string) != dvar->current.string) + if (std::string(value.string) != dvar->current.string) { dvar->current.string = value.string; Game::FS_Restart(0, 0); @@ -1517,7 +1517,7 @@ namespace Components { *i = Utils::String::VA("images/%s", i->data()); - if(FileSystem::File(*i).exists()) + if (FileSystem::File(*i).exists()) { i = images.erase(i); continue; @@ -1552,7 +1552,7 @@ namespace Components unsigned int integer = 0x80000000; Utils::RotLeft(integer, 1); - if(integer != 1) + if (integer != 1) { printf("Error\n"); printf("Bit shifting failed: %X\n", integer); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 121fdca5..9bf202cf 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -78,6 +78,8 @@ namespace Game 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); + Com_ParseOnLine_t Com_ParseOnLine = Com_ParseOnLine_t(0x4C0350); + Com_SkipRestOfLine_t Com_SkipRestOfLine = Com_SkipRestOfLine_t(0x4B8300); Com_SetSpaceDelimited_t Com_SetSpaceDelimited = Com_SetSpaceDelimited_t(0x4FC710); Com_Parse_t Com_Parse = Com_Parse_t(0x474D60); Com_MatchToken_t Com_MatchToken = Com_MatchToken_t(0x447130); @@ -128,6 +130,7 @@ namespace Game Dvar_InfoString_Big_t Dvar_InfoString_Big = Dvar_InfoString_Big_t(0x4D98A0); Dvar_SetCommand_t Dvar_SetCommand = Dvar_SetCommand_t(0x4EE430); Dvar_DisplayableValue_t Dvar_DisplayableValue = Dvar_DisplayableValue_t(0x4B5530); + Dvar_Reset_t Dvar_Reset = Dvar_Reset_t(0x4FEFD0); Encode_Init_t Encode_Init = Encode_Init_t(0x462AB0); @@ -535,6 +538,8 @@ namespace Game FxElemField* s_elemFields = reinterpret_cast(0x73B848); + visField_t* visionDefFields = reinterpret_cast(0x7982F0); // Count 21 + infoParm_t* infoParams = reinterpret_cast(0x79D260); // Count 0x1E XZone* g_zones = reinterpret_cast(0x14C0F80); @@ -1639,8 +1644,8 @@ namespace Game { __asm { - mov eax,[esp+0x4] - mov ebx,0x569950 + mov eax, [esp+0x4] + mov ebx, 0x569950 call ebx retn } @@ -1680,6 +1685,48 @@ namespace Game } } + constexpr auto Dvar_SetFromStringFromSource_Func = 0x648580; + __declspec(naked) void Dvar_SetFromStringFromSource(const dvar_t* /*dvar*/, const char* /*string*/, DvarSetSource /*source*/) + { + __asm + { + pushad + + mov esi, [esp + 0x20 + 0x4] // dvar + mov eax, [esp + 0x20 + 0x8] // string + push [esp + 0x20 + 0xC] // source + call Dvar_SetFromStringFromSource_Func + add esp, 0x4 + + popad + + ret + } + } + + constexpr auto ApplyTokenToField_Func = 0x59A760; + __declspec(naked) bool ApplyTokenToField(unsigned int /*fieldNum*/, const char* /*token*/, visionSetVars_t* /*settings*/) + { + __asm + { + push eax + pushad + + mov eax, [esp + 0x24 + 0x4] // fieldNum + mov ecx, [esp + 0x24 + 0x8] // token + push [esp + 0x24 + 0xC] // settings + call ApplyTokenToField_Func + add esp, 0x4 + + movzx eax, al // Zero extend eax + mov [esp + 0x20], eax + popad + pop eax + + ret + } + } + constexpr auto SV_BotUserMove_Addr = 0x626E50; __declspec(naked) void SV_BotUserMove(client_t* /*client*/) { diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index e521cf0d..5f2fed37 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -169,6 +169,12 @@ namespace Game typedef void(__cdecl * Com_BeginParseSession_t)(const char* filename); extern Com_BeginParseSession_t Com_BeginParseSession; + typedef char*(__cdecl * Com_ParseOnLine_t)(const char** data_p); + extern Com_ParseOnLine_t Com_ParseOnLine; + + typedef void(__cdecl * Com_SkipRestOfLine_t)(const char** data); + extern Com_SkipRestOfLine_t Com_SkipRestOfLine; + typedef void(__cdecl * Com_SetSpaceDelimited_t)(int); extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited; @@ -325,6 +331,9 @@ namespace Game typedef const char*(__cdecl * Dvar_DisplayableValue_t)(const dvar_t* dvar); extern Dvar_DisplayableValue_t Dvar_DisplayableValue; + typedef void(__cdecl * Dvar_Reset_t)(const dvar_t* dvar, DvarSetSource setSource); + extern Dvar_Reset_t Dvar_Reset; + typedef bool(__cdecl * Encode_Init_t)(const char* ); extern Encode_Init_t Encode_Init; @@ -1177,6 +1186,8 @@ namespace Game extern FxElemField* s_elemFields; + extern visField_t* visionDefFields; + extern infoParm_t* infoParams; extern XZone* g_zones; @@ -1339,4 +1350,7 @@ namespace Game void AimAssist_UpdateAdsLerp(const AimInput* input); void Dvar_SetVariant(dvar_t* var, DvarValue value, DvarSetSource source); + void Dvar_SetFromStringFromSource(const dvar_t* dvar, const char* string, DvarSetSource source); + + bool ApplyTokenToField(unsigned int fieldNum, const char* token, visionSetVars_t* settings); } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 0eebd158..cbc87b27 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -1014,6 +1014,7 @@ namespace Game { const char* name; int offset; + // 0 is for int, 1 is for float, otherwise it's a vector int fieldType; }; From d191e90e1fa0ce41c6a549db5deb0925fa4f3e8b Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 22:24:40 +0200 Subject: [PATCH 26/77] [Weapon] Fix null pointer (Prank gone wrong) --- src/Components/Modules/Weapon.cpp | 45 +++++++++++++++++++++++++++++++ src/Components/Modules/Weapon.hpp | 3 +++ 2 files changed, 48 insertions(+) diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index 82e2db4d..58a90e46 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -448,6 +448,48 @@ namespace Components } } + void __declspec(naked) Weapon::CG_UpdatePrimaryForAltModeWeapon_Stub() + { + __asm + { + mov eax, 0x440EB0 // BG_GetWeaponDef + call eax + add esp, 0x4 + + test eax, eax + jz null + + // Game code + push 0x59E349 + retn + + null: + mov al, 1 + ret + } + } + + void __declspec(naked) Weapon::CG_SelectWeaponIndex_Stub() + { + __asm + { + mov eax, 0x440EB0 // BG_GetWeaponDef + call eax + add esp, 0x4 + + test eax, eax + jz null + + // Game code + push 0x48BB2D + retn + + null: + push 0x48BB1F // Exit function + ret + } + } + void __declspec(naked) Weapon::WeaponEntCanBeGrabbed_Stub() { using namespace Game; @@ -525,6 +567,9 @@ namespace Components //Utils::Hook::Nop(0x452C1D, 2); //Utils::Hook::Nop(0x452C24, 5); + Utils::Hook(0x59E341, CG_UpdatePrimaryForAltModeWeapon_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x48BB25, CG_SelectWeaponIndex_Stub, HOOK_JUMP).install()->quick(); + AddScriptMethods(); AssertOffset(Game::playerState_s, Game::playerState_s::weapCommon.weapFlags, 0x2BC); diff --git a/src/Components/Modules/Weapon.hpp b/src/Components/Modules/Weapon.hpp index 12dd99a2..7599c772 100644 --- a/src/Components/Modules/Weapon.hpp +++ b/src/Components/Modules/Weapon.hpp @@ -26,6 +26,9 @@ namespace Components static int ParseWeaponConfigStrings(); static int ClearConfigStrings(void* dest, int value, int size); + static void CG_UpdatePrimaryForAltModeWeapon_Stub(); + static void CG_SelectWeaponIndex_Stub(); + static void WeaponEntCanBeGrabbed_Stub(); static void AddScriptMethods(); }; From aa6a8a29befeb955c3bf18957e3f2fce094e6156 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 30 Jun 2022 23:57:23 +0200 Subject: [PATCH 27/77] [VisionFile] Fix ill-defined loop or something --- src/Components/Modules/VisionFile.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Components/Modules/VisionFile.cpp b/src/Components/Modules/VisionFile.cpp index a030515b..463a9d01 100644 --- a/src/Components/Modules/VisionFile.cpp +++ b/src/Components/Modules/VisionFile.cpp @@ -9,22 +9,14 @@ namespace Components void VisionFile::ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName) { - for (std::size_t i = 0; i < std::extent_v; ++i) + for (std::size_t i = 0; i < ARRAYSIZE(DvarExceptions); ++i) { if (dvarName == DvarExceptions[i]) { const auto* dvar = Game::Dvar_FindVar(dvarName.data()); - - assert(dvar != nullptr); - - const auto* currentVal = Game::Dvar_DisplayableValue(dvar); const auto* parsedValue = Game::Com_ParseOnLine(&buffer); - if (std::strcmp(parsedValue, currentVal) == 0) - { - // The dvar is already set to the value we want - return; - } + assert(dvar != nullptr); Game::Dvar_SetFromStringFromSource(dvar, parsedValue, Game::DvarSetSource::DVAR_SOURCE_INTERNAL); Logger::Print("Overriding '{}' from '{}'\n", dvarName, fileName); From c4af5391ca0e508f5e681b06a549b6fd251732fe Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 1 Jul 2022 19:08:35 +0200 Subject: [PATCH 28/77] [QuickPatch] Better hook for cheat dvars --- src/Components/Modules/QuickPatch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 5efa88a4..564aaa1e 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -683,7 +683,7 @@ namespace Components Utils::Hook::Set(0x60AE2B, true); // Disable cheat protection for dvars - Utils::Hook::Set(0x647682, 0xEB); + Utils::Hook::Set(0x646515, 0xEB); // Dvar_IsCheatProtected #else // Remove missing tag message Utils::Hook::Nop(0x4EBF1A, 5); From 69bbdc0e9d7f26e90bbd89bd16f65b29f44cfffd Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 2 Jul 2022 11:41:59 +0200 Subject: [PATCH 29/77] [Logger] Make debug text less lengthy --- src/Components/Modules/Logger.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 9d9ecf89..c47efb6f 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -50,11 +50,11 @@ namespace Components void Logger::DebugInternal(std::string_view fmt, std::format_args&& args, [[maybe_unused]] const std::source_location& loc) { - const auto msg = std::vformat(fmt, args); #ifdef LOGGER_TRACE + const auto msg = std::vformat(fmt, args); const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line()); #else - const auto out = std::format("Debug:\n {}\n", msg); + const auto out = "^2" + std::vformat(fmt, args); #endif Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); From 4f321a986ff6bee6a82c8c2d062e0a1c90aa2d44 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 2 Jul 2022 11:52:52 +0200 Subject: [PATCH 30/77] Better asm func for R_GetCharacterGlyph --- src/Game/Functions.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 9bf202cf..c9275de6 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -1520,24 +1520,25 @@ namespace Game } } - __declspec(naked) Glyph* R_GetCharacterGlyph(Font_s* /*font*/, unsigned int /*letter*/) + Glyph* R_GetCharacterGlyph(Font_s* font, unsigned int letter) { + static auto R_GetCharacterGlyph_Func = 0x5055C0; + Glyph* returnValue; + __asm { - push eax pushad - mov edi, [esp + 0x8 + 0x24] // letter - push [esp + 0x4 + 0x24] // font - mov eax, 0x5055C0 - call eax + mov edi, letter + push font + call R_GetCharacterGlyph_Func add esp, 4 - mov [esp + 0x20], eax + mov returnValue, eax popad - pop eax - ret } + + return returnValue; } __declspec(naked) bool SetupPulseFXVars(const char* /*text*/, int /*maxLength*/, int /*fxBirthTime*/, int /*fxLetterTime*/, int /*fxDecayStartTime*/, int /*fxDecayDuration*/, bool* /*resultDrawRandChar*/, int* /*resultRandSeed*/, int* /*resultMaxLength*/, bool* /*resultDecaying*/, int* /*resultDecayTimeElapsed*/) From 2040533a0aacd2f56934d2c1a8980b94d4125ec5 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 2 Jul 2022 12:17:08 +0200 Subject: [PATCH 31/77] [Scheduler] Fix hook --- src/Components/Modules/Scheduler.cpp | 6 +++--- src/Components/Modules/Scheduler.hpp | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Components/Modules/Scheduler.cpp b/src/Components/Modules/Scheduler.cpp index 94a778f4..bc29402e 100644 --- a/src/Components/Modules/Scheduler.cpp +++ b/src/Components/Modules/Scheduler.cpp @@ -67,9 +67,9 @@ namespace Components Pipelines[type].execute(); } - void Scheduler::CL_DrawScreen_Hk() + void Scheduler::ScrPlace_EndFrame_Hk() { - Utils::Hook::Call(0x5AC950)(); + Utils::Hook::Call(0x4AA720)(); Execute(Pipeline::RENDERER); } @@ -165,7 +165,7 @@ namespace Components } }); - Utils::Hook(0x5ACB99, CL_DrawScreen_Hk, HOOK_CALL).install()->quick(); + Utils::Hook(0x5ACB9E, ScrPlace_EndFrame_Hk, HOOK_CALL).install()->quick(); // Hook G_Glass_Update so we may fix TLS issues Utils::Hook(0x416049, ServerFrame_Hk, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Scheduler.hpp b/src/Components/Modules/Scheduler.hpp index c445e4ad..72715b06 100644 --- a/src/Components/Modules/Scheduler.hpp +++ b/src/Components/Modules/Scheduler.hpp @@ -59,7 +59,7 @@ namespace Components static void Execute(Pipeline type); - static void CL_DrawScreen_Hk(); + static void ScrPlace_EndFrame_Hk(); static void ServerFrame_Hk(); static void ClientFrame_Hk(int localClientNum); static void MainFrame_Hk(); From 8127aa395eaf7692e25cfc0e149e9d475438f109 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 2 Jul 2022 18:30:35 +0200 Subject: [PATCH 32/77] [Weapon] Fix loading code --- src/Components/Modules/Weapon.cpp | 18 +++++++++--------- src/Components/Modules/Weapon.hpp | 2 +- src/Game/Functions.cpp | 1 + src/Game/Functions.hpp | 7 +++++-- src/Game/Structs.hpp | 2 +- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index 58a90e46..c11b5e2e 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -2,17 +2,15 @@ namespace Components { - Game::XAssetHeader Weapon::WeaponFileLoad(Game::XAssetType /*type*/, const std::string& filename) + Game::WeaponCompleteDef* Weapon::LoadWeaponCompleteDef(const char* name) { - Game::XAssetHeader header = { nullptr }; - - // Try loading raw weapon - if (FileSystem::File(Utils::String::VA("weapons/mp/%s", filename.data())).exists()) + if (auto* rawWeaponFile = Game::BG_LoadWeaponCompleteDefInternal("mp", name)) { - header.data = Game::BG_LoadWeaponDef_LoadObj(filename.data()); + return rawWeaponFile; } - return header; + auto* zoneWeaponFile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_WEAPON, name).weapon; + return Game::DB_IsXAssetDefault(Game::ASSET_TYPE_WEAPON, name) ? nullptr : zoneWeaponFile; } const char* Weapon::GetWeaponConfigString(int index) @@ -542,8 +540,10 @@ namespace Components PatchLimit(); PatchConfigStrings(); - // Intercept weapon loading - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_WEAPON, Weapon::WeaponFileLoad); + // BG_LoadWEaponCompleteDef_FastFile + Utils::Hook(0x57B650, LoadWeaponCompleteDef, HOOK_JUMP).install()->quick(); + // Disable warning if raw weapon file cannot be found + Utils::Hook::Nop(0x57AF60, 5); // weapon asset existence check Utils::Hook::Nop(0x408228, 5); // find asset header diff --git a/src/Components/Modules/Weapon.hpp b/src/Components/Modules/Weapon.hpp index 7599c772..3b6d14d5 100644 --- a/src/Components/Modules/Weapon.hpp +++ b/src/Components/Modules/Weapon.hpp @@ -13,7 +13,7 @@ namespace Components Weapon(); private: - static Game::XAssetHeader WeaponFileLoad(Game::XAssetType type, const std::string& filename); + static Game::WeaponCompleteDef* LoadWeaponCompleteDef(const char* name); static void PatchLimit(); static void* LoadNoneWeaponHook(); static void LoadNoneWeaponHookStub(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 9bf202cf..8877ef02 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -33,6 +33,7 @@ namespace Game BG_GetNumWeapons_t BG_GetNumWeapons = BG_GetNumWeapons_t(0x4F5CC0); BG_GetWeaponName_t BG_GetWeaponName = BG_GetWeaponName_t(0x4E6EC0); BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = BG_LoadWeaponDef_LoadObj_t(0x57B5F0); + BG_LoadWeaponCompleteDefInternal_t BG_LoadWeaponCompleteDefInternal = BG_LoadWeaponCompleteDefInternal_t(0x4B5F10); BG_GetWeaponDef_t BG_GetWeaponDef = BG_GetWeaponDef_t(0x440EB0); BG_GetEntityTypeName_t BG_GetEntityTypeName = BG_GetEntityTypeName_t(0x43A0E0); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 5f2fed37..60654959 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -46,9 +46,12 @@ namespace Game typedef const char*(__cdecl * BG_GetWeaponName_t)(unsigned int index); extern BG_GetWeaponName_t BG_GetWeaponName; - typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename); + typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* name); extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj; + typedef WeaponCompleteDef*(__cdecl * BG_LoadWeaponCompleteDefInternal_t)(const char* folder, const char* name); + extern BG_LoadWeaponCompleteDefInternal_t BG_LoadWeaponCompleteDefInternal; + typedef WeaponDef*(__cdecl * BG_GetWeaponDef_t)(unsigned int weaponIndex); extern BG_GetWeaponDef_t BG_GetWeaponDef; @@ -235,7 +238,7 @@ namespace Game typedef const char *(__cdecl * DB_GetXAssetTypeName_t)(XAssetType type); extern DB_GetXAssetTypeName_t DB_GetXAssetTypeName; - typedef const char *(__cdecl * DB_IsXAssetDefault_t)(XAssetType type, const char* name); + typedef int(__cdecl * DB_IsXAssetDefault_t)(XAssetType type, const char* name); extern DB_IsXAssetDefault_t DB_IsXAssetDefault; typedef void(__cdecl * DB_GetRawBuffer_t)(RawFile* rawfile, char* buffer, int size); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index cbc87b27..09b623c3 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -4455,7 +4455,7 @@ namespace Game IMPACT_TYPE_COUNT = 0xB, }; - struct /*__declspec(align(2))*/ WeaponCompleteDef + struct WeaponCompleteDef { const char *szInternalName; WeaponDef *weapDef; From a200d82ae67ea210255089cde6ccc2dbb0b556b9 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 2 Jul 2022 19:52:57 +0200 Subject: [PATCH 33/77] [Dvar] Better dvar settings for debugging --- src/Components/Modules/Auth.cpp | 2 +- src/Components/Modules/Branding.cpp | 8 +-- src/Components/Modules/Bullet.cpp | 4 +- src/Components/Modules/CardTitles.cpp | 2 +- src/Components/Modules/Chat.cpp | 2 +- src/Components/Modules/Clantags.cpp | 2 +- src/Components/Modules/D3D9Ex.cpp | 2 +- src/Components/Modules/Debug.cpp | 2 +- src/Components/Modules/Dedicated.cpp | 10 ++-- src/Components/Modules/Discovery.cpp | 4 +- src/Components/Modules/Download.cpp | 12 ++--- src/Components/Modules/Dvar.cpp | 68 ++++++++++++++++++------- src/Components/Modules/Dvar.hpp | 9 ++-- src/Components/Modules/FastFiles.cpp | 2 +- src/Components/Modules/Localization.cpp | 2 +- src/Components/Modules/Logger.cpp | 2 +- src/Components/Modules/MapRotation.cpp | 4 +- src/Components/Modules/ModList.cpp | 2 +- src/Components/Modules/Party.cpp | 20 ++++---- src/Components/Modules/Party.hpp | 2 +- src/Components/Modules/PlayerName.cpp | 2 +- src/Components/Modules/QuickPatch.cpp | 4 +- src/Components/Modules/RCon.cpp | 4 +- src/Components/Modules/RawMouse.cpp | 2 +- src/Components/Modules/ServerList.cpp | 12 ++--- src/Components/Modules/TextRenderer.cpp | 14 ++--- src/Components/Modules/Theatre.cpp | 4 +- src/Components/Modules/UIFeeder.cpp | 6 +-- src/Components/Modules/Window.cpp | 4 +- src/Game/Functions.cpp | 1 + src/Game/Functions.hpp | 3 ++ src/Game/Structs.hpp | 2 +- 32 files changed, 129 insertions(+), 90 deletions(-) diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index 0cbdbce6..01f87c59 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -432,7 +432,7 @@ namespace Components Scheduler::Loop(Auth::Frame, Scheduler::Pipeline::MAIN); // Register dvar - Dvar::Register("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_SERVERINFO, "Security level for GUID certificates (POW)"); + Dvar::Register("sv_securityLevel", 23, 0, 512, Game::DVAR_SERVERINFO, "Security level for GUID certificates (POW)"); // Install registration hook Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/Branding.cpp b/src/Components/Modules/Branding.cpp index 0b2a0c34..e878e625 100644 --- a/src/Components/Modules/Branding.cpp +++ b/src/Components/Modules/Branding.cpp @@ -75,7 +75,7 @@ namespace Components [[maybe_unused]] float y, float min, float max, [[maybe_unused]] int flags, const char* description) { return Game::Dvar_RegisterVec2(dvarName, -60.0f, - 474.0f, min, max, Game::dvar_flag::DVAR_ROM, description); + 474.0f, min, max, Game::DVAR_ROM, description); } void Branding::RegisterBrandingDvars() @@ -86,11 +86,11 @@ namespace Components constexpr auto value = false; #endif Branding::CGDrawVersion = Dvar::Register("cg_drawVersion", value, - Game::dvar_flag::DVAR_NONE, "Draw the game version"); + Game::DVAR_NONE, "Draw the game version"); Branding::CGDrawVersionX = Dvar::Register("cg_drawVersionX", 10.0f, - 0.0f, 512.0f, Game::dvar_flag::DVAR_NONE, "X offset for the version string"); + 0.0f, 512.0f, Game::DVAR_NONE, "X offset for the version string"); Branding::CGDrawVersionY = Dvar::Register("cg_drawVersionY", 455.0f, - 0.0f, 512.0f, Game::dvar_flag::DVAR_NONE, "Y offset for the version string"); + 0.0f, 512.0f, Game::DVAR_NONE, "Y offset for the version string"); } Branding::Branding() diff --git a/src/Components/Modules/Bullet.cpp b/src/Components/Modules/Bullet.cpp index b8ec1338..fe18981a 100644 --- a/src/Components/Modules/Bullet.cpp +++ b/src/Components/Modules/Bullet.cpp @@ -45,10 +45,10 @@ namespace Components Bullet::Bullet() { BGSurfacePenetration = Dvar::Register("bg_surfacePenetration", 0.0f, - 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, + 0.0f, std::numeric_limits::max(), Game::DVAR_CODINFO, "Set to a value greater than 0 to override the surface penetration depth"); BGBulletRange = Game::Dvar_RegisterFloat("bg_bulletRange", 8192.0f, - 0.0f, std::numeric_limits::max(), Game::dvar_flag::DVAR_CODINFO, + 0.0f, std::numeric_limits::max(), Game::DVAR_CODINFO, "Max range used when calculating the bullet end position"); Utils::Hook(0x4F6980, BG_GetSurfacePenetrationDepthStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index c5eae749..78e7a2f9 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -192,7 +192,7 @@ namespace Components { Scheduler::Once([] { - CardTitles::CustomTitleDvar = Dvar::Register("customtitle", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, "Custom card title"); + CardTitles::CustomTitleDvar = Dvar::Register("customtitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title"); }, Scheduler::Pipeline::MAIN); ServerCommands::OnCommand(21, [](Command::Params* params) diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index ead9e87b..2e66a925 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -410,7 +410,7 @@ namespace Components Chat::Chat() { cg_chatWidth = Dvar::Register("cg_chatWidth", 52, 1, std::numeric_limits::max(), Game::DVAR_ARCHIVE, "The normalized maximum width of a chat message"); - sv_disableChat = Dvar::Register("sv_disableChat", false, Game::dvar_flag::DVAR_NONE, "Disable chat messages from clients"); + sv_disableChat = Dvar::Register("sv_disableChat", false, Game::DVAR_NONE, "Disable chat messages from clients"); Events::OnSVInit(AddChatCommands); // Intercept chat sending diff --git a/src/Components/Modules/Clantags.cpp b/src/Components/Modules/Clantags.cpp index c137ef97..eac6a916 100644 --- a/src/Components/Modules/Clantags.cpp +++ b/src/Components/Modules/Clantags.cpp @@ -75,7 +75,7 @@ namespace Components // Create clantag dvar Scheduler::Once([] { - Dvar::Register("clantag", "", Game::dvar_flag::DVAR_USERINFO | Game::dvar_flag::DVAR_ARCHIVE, + Dvar::Register("clantag", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "If set, your clantag will be shown on the scoreboard."); }, Scheduler::Pipeline::MAIN); diff --git a/src/Components/Modules/D3D9Ex.cpp b/src/Components/Modules/D3D9Ex.cpp index f6b84844..6903e23a 100644 --- a/src/Components/Modules/D3D9Ex.cpp +++ b/src/Components/Modules/D3D9Ex.cpp @@ -748,7 +748,7 @@ namespace Components { if (Dedicated::IsEnabled()) return; - Dvar::Register("r_useD3D9Ex", false, Game::dvar_flag::DVAR_ARCHIVE, "Use extended d3d9 interface!"); + Dvar::Register("r_useD3D9Ex", false, Game::DVAR_ARCHIVE, "Use extended d3d9 interface!"); // Hook Interface creation Utils::Hook::Set(0x6D74D0, D3D9Ex::Direct3DCreate9Stub); diff --git a/src/Components/Modules/Debug.cpp b/src/Components/Modules/Debug.cpp index 25032d40..2821d58c 100644 --- a/src/Components/Modules/Debug.cpp +++ b/src/Components/Modules/Debug.cpp @@ -227,7 +227,7 @@ namespace Components }; DebugOverlay = Game::Dvar_RegisterEnum("debugOverlay", debugOverlayNames_0, 0, - Game::dvar_flag::DVAR_NONE, "Toggles the display of various debug info."); + Game::DVAR_NONE, "Toggles the display of various debug info."); } Debug::Debug() diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 22f49aef..5e475633 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -135,7 +135,7 @@ namespace Components Game::dvar_t* Dedicated::Dvar_RegisterSVNetworkFps(const char* dvarName, int, int min, int, int, const char* description) { - return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::dvar_flag::DVAR_NONE, description); + return Game::Dvar_RegisterInt(dvarName, 1000, min, 1000, Game::DVAR_NONE, description); } void Dedicated::AddDedicatedCommands() @@ -206,7 +206,7 @@ namespace Components Dedicated::Dedicated() { Dedicated::COMLogFilter = Dvar::Register("com_logFilter", true, - Game::dvar_flag::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); + Game::DVAR_LATCH, "Removes ~95% of unneeded lines from the log"); if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) { @@ -214,7 +214,7 @@ namespace Components Scheduler::Loop(Steam::SteamAPI_RunCallbacks, Scheduler::Pipeline::SERVER); Dedicated::SVLanOnly = Dvar::Register("sv_lanOnly", false, - Game::dvar_flag::DVAR_NONE, "Don't act as node"); + Game::DVAR_NONE, "Don't act as node"); Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick(); @@ -281,8 +281,8 @@ namespace Components { Scheduler::Once([] { - Dvar::Register("sv_sayName", "^7Console", Game::dvar_flag::DVAR_NONE, "The name to pose as for 'say' commands"); - Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_NONE, "A custom message of the day for servers"); + Dvar::Register("sv_sayName", "^7Console", Game::DVAR_NONE, "The name to pose as for 'say' commands"); + Dvar::Register("sv_motd", "", Game::DVAR_NONE, "A custom message of the day for servers"); }, Scheduler::Pipeline::MAIN); Events::OnSVInit(Dedicated::AddDedicatedCommands); diff --git a/src/Components/Modules/Discovery.cpp b/src/Components/Modules/Discovery.cpp index a97502ea..7c4c243d 100644 --- a/src/Components/Modules/Discovery.cpp +++ b/src/Components/Modules/Discovery.cpp @@ -14,8 +14,8 @@ namespace Components Discovery::Discovery() { - Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_ARCHIVE, "Minimum scan range port for local server discovery"); - Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::dvar_flag::DVAR_ARCHIVE, "Maximum scan range port for local server discovery"); + Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::DVAR_ARCHIVE, "Minimum scan range port for local server discovery"); + Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::DVAR_ARCHIVE, "Maximum scan range port for local server discovery"); // An additional thread prevents lags // Not sure if that's the best way though diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 6cda7c4c..d75b90f4 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -903,9 +903,9 @@ namespace Components { Scheduler::Once([] { - Dvar::Register("ui_dl_timeLeft", "", Game::dvar_flag::DVAR_NONE, ""); - Dvar::Register("ui_dl_progress", "", Game::dvar_flag::DVAR_NONE, ""); - Dvar::Register("ui_dl_transRate", "", Game::dvar_flag::DVAR_NONE, ""); + Dvar::Register("ui_dl_timeLeft", "", Game::DVAR_NONE, ""); + Dvar::Register("ui_dl_progress", "", Game::DVAR_NONE, ""); + Dvar::Register("ui_dl_transRate", "", Game::DVAR_NONE, ""); }, Scheduler::Pipeline::MAIN); UIScript::Add("mod_download_cancel", [](UIScript::Token) @@ -916,13 +916,13 @@ namespace Components Scheduler::Once([] { - Dvar::Register("sv_wwwDownload", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to enable downloading maps/mods from an external server."); - Dvar::Register("sv_wwwBaseUrl", "", Game::dvar_flag::DVAR_ARCHIVE, "Set to the base url for the external map download."); + Dvar::Register("sv_wwwDownload", false, Game::DVAR_ARCHIVE, "Set to true to enable downloading maps/mods from an external server."); + Dvar::Register("sv_wwwBaseUrl", "", Game::DVAR_ARCHIVE, "Set to the base url for the external map download."); // Force users to enable this because we don't want to accidentally turn everyone's pc into a http server into all their files again // not saying we are but ya know... accidents happen // by having it saved we force the user to enable it in config_mp because it only checks the dvar on startup to see if we should init download or not - Dvar::Register("mod_force_download_server", false, Game::dvar_flag::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches)."); + Dvar::Register("mod_force_download_server", false, Game::DVAR_ARCHIVE, "Set to true to force the client to run the download server for mods (for mods in private matches)."); }, Scheduler::Pipeline::MAIN); Scheduler::Loop([] diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index 5a8fb845..4334dd59 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -52,14 +52,14 @@ namespace Components template <> unsigned int Dvar::Var::get() { if (this->dvar == nullptr) - return 0u; + return 0; if (this->dvar->type == Game::dvar_type::DVAR_TYPE_INT) { return this->dvar->current.unsignedInt; } - return 0u; + return 0; } template <> float Dvar::Var::get() @@ -77,7 +77,7 @@ namespace Components template <> float* Dvar::Var::get() { - static Game::vec4_t vector{ 0.f, 0.f, 0.f, 0.f }; + static Game::vec4_t vector{0.f, 0.f, 0.f, 0.f}; if (this->dvar == nullptr) return vector; @@ -249,7 +249,7 @@ namespace Components } } - return Dvar::Register(name, username.data(), flags | Game::dvar_flag::DVAR_ARCHIVE, description).get(); + return Dvar::Register(name, username.data(), flags | Game::DVAR_ARCHIVE, description).get(); } void Dvar::SetFromStringByNameSafeExternal(const char* dvarName, const char* string) @@ -311,7 +311,7 @@ namespace Components { // Save the dvar original value if it has the archive flag const auto* dvar = Game::Dvar_FindVar(dvarName); - if (dvar != nullptr && dvar->flags & Game::dvar_flag::DVAR_ARCHIVE) + if (dvar != nullptr && dvar->flags & Game::DVAR_ARCHIVE) { if (Dvar::AreArchiveDvarsProtected()) { @@ -328,28 +328,56 @@ namespace Components Utils::Hook::Call(0x4F52E0)(dvarName, value); } + void Dvar::OnRegisterVariant([[maybe_unused]] Game::dvar_t* dvar) + { +#ifdef _DEBUG + dvar->flags &= ~Game::DVAR_CHEAT; +#endif + } + + __declspec(naked) void Dvar::Dvar_RegisterVariant_Stub() + { + __asm + { + pushad + + push eax + call Dvar::OnRegisterVariant + add esp, 0x4 + + popad + + // Game's code + pop edi + pop esi + pop ebp + pop ebx + ret + } + } + Dvar::Dvar() { // set flags of cg_drawFPS to archive - Utils::Hook::Or(0x4F8F69, Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Or(0x4F8F69, Game::DVAR_ARCHIVE); // un-cheat camera_thirdPersonCrosshairOffset and add archive flags - Utils::Hook::Xor(0x447B41, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Xor(0x447B41, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); // un-cheat cg_fov and add archive flags - Utils::Hook::Xor(0x4F8E35, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Xor(0x4F8E35, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); // un-cheat cg_fovscale and add archive flags - Utils::Hook::Xor(0x4F8E68, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Xor(0x4F8E68, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); // un-cheat cg_debugInfoCornerOffset and add archive flags - Utils::Hook::Xor(0x4F8FC2, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Xor(0x4F8FC2, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); // remove archive flags for cg_hudchatposition - Utils::Hook::Xor(0x4F9992, Game::dvar_flag::DVAR_ARCHIVE); + Utils::Hook::Xor(0x4F9992, Game::DVAR_ARCHIVE); // remove write protection from fs_game - Utils::Hook::Xor(0x6431EA, Game::dvar_flag::DVAR_INIT); + Utils::Hook::Xor(0x6431EA, Game::DVAR_INIT); // set cg_fov max to 160.0 // because that's the max on SP @@ -361,19 +389,19 @@ namespace Components Utils::Hook::Set(0x408078, &volume); // Uncheat ui_showList - Utils::Hook::Xor(0x6310DC, Game::dvar_flag::DVAR_CHEAT); + Utils::Hook::Xor(0x6310DC, Game::DVAR_CHEAT); // Uncheat ui_debugMode - Utils::Hook::Xor(0x6312DE, Game::dvar_flag::DVAR_CHEAT); + Utils::Hook::Xor(0x6312DE, Game::DVAR_CHEAT); // Hook dvar 'name' registration Utils::Hook(0x40531C, Dvar::Dvar_RegisterName, HOOK_CALL).install()->quick(); // un-cheat safeArea_* and add archive flags - Utils::Hook::Xor(0x42E3F5, Game::dvar_flag::DVAR_ROM | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_horizontal - Utils::Hook::Xor(0x42E423, Game::dvar_flag::DVAR_ROM | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_adjusted_vertical - Utils::Hook::Xor(0x42E398, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_horizontal - Utils::Hook::Xor(0x42E3C4, Game::dvar_flag::DVAR_CHEAT | Game::dvar_flag::DVAR_ARCHIVE); //safeArea_vertical + Utils::Hook::Xor(0x42E3F5, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_horizontal + Utils::Hook::Xor(0x42E423, Game::DVAR_ROM | Game::DVAR_ARCHIVE); //safeArea_adjusted_vertical + Utils::Hook::Xor(0x42E398, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_horizontal + Utils::Hook::Xor(0x42E3C4, Game::DVAR_CHEAT | Game::DVAR_ARCHIVE); //safeArea_vertical // Don't allow setting cheat protected dvars via menus Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick(); @@ -404,6 +432,10 @@ namespace Components // Reset archive dvars when client leaves a server Events::OnSteamDisconnect(Dvar::ResetDvarsValue); + + // For debugging + Utils::Hook(0x6483FA, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x648438, Dvar::Dvar_RegisterVariant_Stub, HOOK_JUMP).install()->quick(); } Dvar::~Dvar() diff --git a/src/Components/Modules/Dvar.hpp b/src/Components/Modules/Dvar.hpp index fb51fbcb..b457f16b 100644 --- a/src/Components/Modules/Dvar.hpp +++ b/src/Components/Modules/Dvar.hpp @@ -8,10 +8,10 @@ namespace Components class Flag { public: - Flag(Game::dvar_flag flag) : val(flag) {} - Flag(unsigned __int16 flag) : Flag(static_cast(flag)) {} + Flag(Game::DvarFlags flag) : val(flag) {} + Flag(unsigned __int16 flag) : Flag(static_cast(flag)) {} - Game::dvar_flag val; + Game::DvarFlags val; }; class Var @@ -60,5 +60,8 @@ namespace Components static bool AreArchiveDvarsProtected(); static void SaveArchiveDvar(const Game::dvar_t* var); static void DvarSetFromStringByNameStub(const char* dvarName, const char* value); + + static void OnRegisterVariant(Game::dvar_t* dvar); + static void Dvar_RegisterVariant_Stub(); }; } diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index 5ac44386..fedb64d3 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -495,7 +495,7 @@ namespace Components FastFiles::FastFiles() { - Dvar::Register("ui_zoneDebug", false, Game::dvar_flag::DVAR_ARCHIVE, "Display current loaded zone."); + Dvar::Register("ui_zoneDebug", false, Game::DVAR_ARCHIVE, "Display current loaded zone."); // Fix XSurface assets Utils::Hook(0x0048E8A5, FastFiles::Load_XSurfaceArray, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index a3c87efb..8d43f8ea 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -277,7 +277,7 @@ namespace Components // Overwrite SetString Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick(); - Localization::UseLocalization = Dvar::Register("ui_localize", true, Game::dvar_flag::DVAR_NONE, "Use localization strings"); + Localization::UseLocalization = Dvar::Register("ui_localize", true, Game::DVAR_NONE, "Use localization strings"); // Generate localized entries for custom classes above 10 AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, const std::string& name, bool* /*restrict*/) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index c47efb6f..19f29235 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -364,7 +364,7 @@ namespace Components Logger::Logger() { - Dvar::Register("iw4x_onelog", false, Game::dvar_flag::DVAR_LATCH | Game::dvar_flag::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder"); + Dvar::Register("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder"); Utils::Hook(0x642139, Logger::BuildOSPathStub, HOOK_JUMP).install()->quick(); Logger::PipeOutput(nullptr); diff --git a/src/Components/Modules/MapRotation.cpp b/src/Components/Modules/MapRotation.cpp index 8c6658c4..5f5210f1 100644 --- a/src/Components/Modules/MapRotation.cpp +++ b/src/Components/Modules/MapRotation.cpp @@ -306,9 +306,9 @@ namespace Components Utils::Hook::Set(0x4152E8, SV_MapRotate_f); SVRandomMapRotation = Dvar::Register("sv_randomMapRotation", false, - Game::dvar_flag::DVAR_ARCHIVE, "Randomize map rotation when true"); + Game::DVAR_ARCHIVE, "Randomize map rotation when true"); SVDontRotate = Dvar::Register("sv_dontRotate", false, - Game::dvar_flag::DVAR_NONE, "Do not perform map rotation"); + Game::DVAR_NONE, "Do not perform map rotation"); } bool MapRotation::unitTest() diff --git a/src/Components/Modules/ModList.cpp b/src/Components/Modules/ModList.cpp index 44b983ef..6d4db41b 100644 --- a/src/Components/Modules/ModList.cpp +++ b/src/Components/Modules/ModList.cpp @@ -93,7 +93,7 @@ namespace Components if (Dedicated::IsEnabled()) return; ModList::CurrentMod = 0; - Dvar::Register("cl_modVidRestart", true, Game::dvar_flag::DVAR_ARCHIVE, "Perform a vid_restart when loading a mod."); + Dvar::Register("cl_modVidRestart", true, Game::DVAR_ARCHIVE, "Perform a vid_restart when loading a mod."); UIScript::Add("LoadMods", ModList::UIScript_LoadMods); UIScript::Add("RunMod", ModList::UIScript_RunMod); diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 7f293e4f..4a0a5524 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -76,9 +76,9 @@ namespace Components return Party::Container.motd; } - Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::dvar_flag flag, const char* description) + Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::DvarFlags flag, const char* description) { - return Dvar::Register(name, 1, 1, max, Game::dvar_flag::DVAR_INIT | flag, description).get(); + return Dvar::Register(name, 1, 1, max, Game::DVAR_INIT | flag, description).get(); } bool Party::PlaylistAwaiting() @@ -154,8 +154,8 @@ namespace Components Party::Party() { - Party::PartyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_NONE, "Enable party system"); - Dvar::Register("xblive_privatematch", true, Game::dvar_flag::DVAR_INIT, ""); + Party::PartyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::DVAR_NONE, "Enable party system"); + Dvar::Register("xblive_privatematch", true, Game::DVAR_INIT, ""); // various changes to SV_DirectConnect-y stuff to allow non-party joinees Utils::Hook::Set(0x460D96, 0x90E9); @@ -254,12 +254,12 @@ namespace Components Utils::Hook::Set(0x5E3772, "sv_maxclients"); // Unlatch maxclient dvars - Utils::Hook::Xor(0x426187, Game::dvar_flag::DVAR_LATCH); - Utils::Hook::Xor(0x4D374E, Game::dvar_flag::DVAR_LATCH); - Utils::Hook::Xor(0x5E376A, Game::dvar_flag::DVAR_LATCH); - Utils::Hook::Xor(0x4261A1, Game::dvar_flag::DVAR_LATCH); - Utils::Hook::Xor(0x4D376D, Game::dvar_flag::DVAR_LATCH); - Utils::Hook::Xor(0x5E3789, Game::dvar_flag::DVAR_LATCH); + Utils::Hook::Xor(0x426187, Game::DVAR_LATCH); + Utils::Hook::Xor(0x4D374E, Game::DVAR_LATCH); + Utils::Hook::Xor(0x5E376A, Game::DVAR_LATCH); + Utils::Hook::Xor(0x4261A1, Game::DVAR_LATCH); + Utils::Hook::Xor(0x4D376D, Game::DVAR_LATCH); + Utils::Hook::Xor(0x5E3789, Game::DVAR_LATCH); // Patch Live_PlayerHasLoopbackAddr //Utils::Hook::Set(0x418F30, 0x90C3C033); diff --git a/src/Components/Modules/Party.hpp b/src/Components/Modules/Party.hpp index f1d32b09..dada08a8 100644 --- a/src/Components/Modules/Party.hpp +++ b/src/Components/Modules/Party.hpp @@ -50,7 +50,7 @@ namespace Components static SteamID GenerateLobbyId(); - static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description); + static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::DvarFlags flag, const char* description); static DWORD UIDvarIntStub(char* dvar); }; diff --git a/src/Components/Modules/PlayerName.cpp b/src/Components/Modules/PlayerName.cpp index 5f29acc0..3233c279 100644 --- a/src/Components/Modules/PlayerName.cpp +++ b/src/Components/Modules/PlayerName.cpp @@ -106,7 +106,7 @@ namespace Components PlayerName::PlayerName() { - sv_allowColoredNames = Dvar::Register("sv_allowColoredNames", true, Game::dvar_flag::DVAR_NONE, "Allow colored names on the server"); + sv_allowColoredNames = Dvar::Register("sv_allowColoredNames", true, Game::DVAR_NONE, "Allow colored names on the server"); // Disable SV_UpdateUserinfo_f, to block changing the name ingame Utils::Hook::Set(0x6258D0, 0xC3); diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 5efa88a4..7e987eb4 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -248,7 +248,7 @@ namespace Components Utils::Hook(0x4F66A3, CL_KeyEvent_ConsoleEscape_Stub, HOOK_JUMP).install()->quick(); // Intermission time dvar - Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::dvar_flag::DVAR_NONE, "Time in seconds before match server loads the next map"); + Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_NONE, "Time in seconds before match server loads the next map"); g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_CODINFO, "Perform antilag"); Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick(); @@ -328,7 +328,7 @@ namespace Components // Numerical ping (cg_scoreboardPingText 1) Utils::Hook::Set(0x45888E, 1); - Utils::Hook::Set(0x45888C, Game::dvar_flag::DVAR_CHEAT); + Utils::Hook::Set(0x45888C, Game::DVAR_CHEAT); // increase font sizes for chat on higher resolutions static float float13 = 13.0f; diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index 894065be..5a16e35d 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -78,8 +78,8 @@ namespace Components Scheduler::Once([] { - RCon::RconPassword = Dvar::Register("rcon_password", "", Game::dvar_flag::DVAR_NONE, "The password for rcon"); - RCon::RconLogRequests = Dvar::Register("rcon_log_requests", false, Game::dvar_flag::DVAR_NONE, "Print remote commands in the output log"); + RCon::RconPassword = Dvar::Register("rcon_password", "", Game::DVAR_NONE, "The password for rcon"); + RCon::RconLogRequests = Dvar::Register("rcon_log_requests", false, Game::DVAR_NONE, "Print remote commands in the output log"); }, Scheduler::Pipeline::MAIN); Network::OnPacket("rcon", [](const Network::Address& address, [[maybe_unused]] const std::string& data) diff --git a/src/Components/Modules/RawMouse.cpp b/src/Components/Modules/RawMouse.cpp index dfd8575a..f48d8f33 100644 --- a/src/Components/Modules/RawMouse.cpp +++ b/src/Components/Modules/RawMouse.cpp @@ -149,7 +149,7 @@ namespace Components Utils::Hook(0x467C03, RawMouse::IN_Init, HOOK_CALL).install()->quick(); Utils::Hook(0x64D095, RawMouse::IN_Init, HOOK_JUMP).install()->quick(); - RawMouse::M_RawInput = Dvar::Register("m_rawinput", true, Game::dvar_flag::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled."); + RawMouse::M_RawInput = Dvar::Register("m_rawinput", true, Game::DVAR_ARCHIVE, "Use raw mouse input, Improves accuracy & has better support for higher polling rates. Use in_restart to take effect if not enabled."); Window::OnWndMessage(WM_INPUT, RawMouse::OnRawInput); Window::OnCreate(RawMouse::IN_RawMouse_Init); diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index 691f64e1..6397fd52 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -779,14 +779,14 @@ namespace Components Scheduler::Once([] { ServerList::UIServerSelected = Dvar::Register("ui_serverSelected", false, - Game::dvar_flag::DVAR_NONE, "Whether a server has been selected in the serverlist"); + Game::DVAR_NONE, "Whether a server has been selected in the serverlist"); ServerList::UIServerSelectedMap = Dvar::Register("ui_serverSelectedMap", "mp_afghan", - Game::dvar_flag::DVAR_NONE, "Map of the selected server"); + Game::DVAR_NONE, "Map of the selected server"); ServerList::NETServerQueryLimit = Dvar::Register("net_serverQueryLimit", 1, - 1, 10, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server queries per frame"); + 1, 10, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server queries per frame"); ServerList::NETServerFrames = Dvar::Register("net_serverFrames", 30, - 1, 60, Dedicated::IsEnabled() ? Game::dvar_flag::DVAR_NONE : Game::dvar_flag::DVAR_ARCHIVE, "Amount of server query frames per second"); + 1, 60, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server query frames per second"); }, Scheduler::Pipeline::MAIN); // Fix ui_netsource dvar @@ -829,8 +829,8 @@ namespace Components // Set default masterServerName + port and save it Utils::Hook::Set(0x60AD92, "master.xlabs.dev"); - Utils::Hook::Set(0x60AD90, Game::dvar_flag::DVAR_NONE); // masterServerName - Utils::Hook::Set(0x60ADC6, Game::dvar_flag::DVAR_NONE); // masterPort + Utils::Hook::Set(0x60AD90, Game::DVAR_NONE); // masterServerName + Utils::Hook::Set(0x60ADC6, Game::DVAR_NONE); // masterPort // Add server list feeder UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer); diff --git a/src/Components/Modules/TextRenderer.cpp b/src/Components/Modules/TextRenderer.cpp index 780912ff..0fb0a268 100644 --- a/src/Components/Modules/TextRenderer.cpp +++ b/src/Components/Modules/TextRenderer.cpp @@ -1599,10 +1599,10 @@ namespace Components { currentColorTable = &colorTableDefault; - cg_newColors = Dvar::Register("cg_newColors", true, Game::dvar_flag::DVAR_ARCHIVE, "Use Warfare 2 color code style."); - cg_fontIconAutocomplete = Dvar::Register("cg_fontIconAutocomplete", true, Game::dvar_flag::DVAR_ARCHIVE, "Show autocomplete for fonticons when typing."); - cg_fontIconAutocompleteHint = Dvar::Register("cg_fontIconAutocompleteHint", true, Game::dvar_flag::DVAR_ARCHIVE, "Show hint text in autocomplete for fonticons."); - sv_customTextColor = Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_CODINFO, "Color for the extended color code."); + cg_newColors = Dvar::Register("cg_newColors", true, Game::DVAR_ARCHIVE, "Use Warfare 2 color code style."); + cg_fontIconAutocomplete = Dvar::Register("cg_fontIconAutocomplete", true, Game::DVAR_ARCHIVE, "Show autocomplete for fonticons when typing."); + cg_fontIconAutocompleteHint = Dvar::Register("cg_fontIconAutocompleteHint", true, Game::DVAR_ARCHIVE, "Show hint text in autocomplete for fonticons."); + sv_customTextColor = Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::DVAR_CODINFO, "Color for the extended color code."); // Initialize font icons when initializing UI Utils::Hook(0x4B5422, UI_Init_Hk, HOOK_CALL).install()->quick(); @@ -1617,11 +1617,11 @@ namespace Components Utils::Hook(0x417770, ColorIndex, HOOK_JUMP).install()->quick(); // Add a colorblind mode for team colors - r_colorBlind = Dvar::Register("r_colorBlind", false, Game::dvar_flag::DVAR_ARCHIVE, "Use color-blindness-friendly colors"); + r_colorBlind = Dvar::Register("r_colorBlind", false, Game::DVAR_ARCHIVE, "Use color-blindness-friendly colors"); // A dark red - g_ColorBlind_EnemyTeam = Game::Dvar_RegisterColor("g_ColorBlind_EnemyTeam", 0.659f, 0.088f, 0.145f, 1, Game::dvar_flag::DVAR_ARCHIVE, "Enemy team color for colorblind mode"); + g_ColorBlind_EnemyTeam = Game::Dvar_RegisterColor("g_ColorBlind_EnemyTeam", 0.659f, 0.088f, 0.145f, 1, Game::DVAR_ARCHIVE, "Enemy team color for colorblind mode"); // A bright yellow - g_ColorBlind_MyTeam = Game::Dvar_RegisterColor("g_ColorBlind_MyTeam", 1, 0.859f, 0.125f, 1, Game::dvar_flag::DVAR_ARCHIVE, "Ally team color for colorblind mode"); + g_ColorBlind_MyTeam = Game::Dvar_RegisterColor("g_ColorBlind_MyTeam", 1, 0.859f, 0.125f, 1, Game::DVAR_ARCHIVE, "Ally team color for colorblind mode"); // Replace team colors with colorblind team colors when colorblind is enabled Utils::Hook(0x406530, GetUnpackedColorByNameStub, HOOK_JUMP).install()->quick(); diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index 855a54e9..46285135 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -342,8 +342,8 @@ namespace Components Theatre::Theatre() { - Dvar::Register("cl_autoRecord", true, Game::dvar_flag::DVAR_ARCHIVE, "Automatically record games."); - Dvar::Register("cl_demosKeep", 30, 1, 999, Game::dvar_flag::DVAR_ARCHIVE, "How many demos to keep with autorecord."); + Dvar::Register("cl_autoRecord", true, Game::DVAR_ARCHIVE, "Automatically record games."); + Dvar::Register("cl_demosKeep", 30, 1, 999, Game::DVAR_ARCHIVE, "How many demos to keep with autorecord."); Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick(); Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/UIFeeder.cpp b/src/Components/Modules/UIFeeder.cpp index 292428da..cefc0400 100644 --- a/src/Components/Modules/UIFeeder.cpp +++ b/src/Components/Modules/UIFeeder.cpp @@ -383,9 +383,9 @@ namespace Components Scheduler::Once([] { - Dvar::Register("ui_map_long", "Afghan", Game::dvar_flag::DVAR_NONE, ""); - Dvar::Register("ui_map_name", "mp_afghan", Game::dvar_flag::DVAR_NONE, ""); - Dvar::Register("ui_map_desc", "", Game::dvar_flag::DVAR_NONE, ""); + Dvar::Register("ui_map_long", "Afghan", Game::DVAR_NONE, ""); + Dvar::Register("ui_map_name", "mp_afghan", Game::DVAR_NONE, ""); + Dvar::Register("ui_map_desc", "", Game::DVAR_NONE, ""); }, Scheduler::Pipeline::MAIN); // Get feeder item count diff --git a/src/Components/Modules/Window.cpp b/src/Components/Modules/Window.cpp index e725d397..f044ea08 100644 --- a/src/Components/Modules/Window.cpp +++ b/src/Components/Modules/Window.cpp @@ -165,8 +165,8 @@ namespace Components Window::Window() { // Borderless window - Window::NoBorder = Dvar::Register("r_noborder", true, Game::dvar_flag::DVAR_ARCHIVE, "Do not use a border in windowed mode"); - Window::NativeCursor = Dvar::Register("ui_nativeCursor", false, Game::dvar_flag::DVAR_ARCHIVE, "Display native cursor"); + Window::NoBorder = Dvar::Register("r_noborder", true, Game::DVAR_ARCHIVE, "Do not use a border in windowed mode"); + Window::NativeCursor = Dvar::Register("ui_nativeCursor", false, Game::DVAR_ARCHIVE, "Display native cursor"); Utils::Hook(0x507643, Window::StyleHookStub, HOOK_CALL).install()->quick(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 9bf202cf..84ebd2ce 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -124,6 +124,7 @@ namespace Game Dvar_RegisterEnum_t Dvar_RegisterEnum = Dvar_RegisterEnum_t(0x412E40); Dvar_RegisterString_t Dvar_RegisterString = Dvar_RegisterString_t(0x4FC7E0); Dvar_RegisterColor_t Dvar_RegisterColor = Dvar_RegisterColor_t(0x4F28E0); + Dvar_RegisterVec3Color_t Dvar_RegisterVec3Color = Dvar_RegisterVec3Color_t(0x4918B0); Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = Dvar_GetUnpackedColorByName_t(0x406530); Dvar_FindVar_t Dvar_FindVar = Dvar_FindVar_t(0x4D5390); diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 5f2fed37..a4eceabb 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -295,6 +295,9 @@ namespace Game typedef dvar_t*(__cdecl * Dvar_RegisterColor_t)(const char* dvarName, float r, float g, float b, float a, unsigned __int16 flags, const char* description); extern Dvar_RegisterColor_t Dvar_RegisterColor; + typedef dvar_t*(__cdecl * Dvar_RegisterVec3Color_t)(const char* dvarName, float x, float y, float z, float max, unsigned __int16 flags, const char* description); + extern Dvar_RegisterVec3Color_t Dvar_RegisterVec3Color; + typedef void(__cdecl * Dvar_SetFromStringByName_t)(const char* dvarName, const char* string); extern Dvar_SetFromStringByName_t Dvar_SetFromStringByName; diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index cbc87b27..f54bbff3 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -133,7 +133,7 @@ namespace Game SURF_TYPE_COUNT }; - enum dvar_flag : unsigned __int16 + enum DvarFlags : unsigned __int16 { DVAR_NONE = 0, // No flags DVAR_ARCHIVE = 1 << 0, // Set to cause it to be saved to config_mp.cfg of the client From e4ec3092beb3e235dde1168ec7c27dcf0a0a224d Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 3 Jul 2022 17:49:22 +0200 Subject: [PATCH 34/77] [AssetHandler] Fix gfxworld issue with zones 316 or higher --- src/Components/Modules/AssetHandler.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 24dc0051..04079444 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -269,6 +269,13 @@ namespace Components } } } + + if (type == Game::XAssetType::ASSET_TYPE_GFXWORLD && Zones::Version() >= 316) + { + asset.gfxWorld->sortKeyEffectDecal = 39; + asset.gfxWorld->sortKeyEffectAuto = 48; + asset.gfxWorld->sortKeyDistortion = 43; + } } bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset) From 474d9df0142cb314234ec75340db9ce3731700eb Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 3 Jul 2022 21:46:42 +0200 Subject: [PATCH 35/77] [Logger] Add new line. Remove gren text --- src/Components/Modules/Logger.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 19f29235..3acd6f93 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -54,7 +54,7 @@ namespace Components const auto msg = std::vformat(fmt, args); const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line()); #else - const auto out = "^2" + std::vformat(fmt, args); + const auto out = std::vformat(fmt, args) + "\n"; #endif Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); From abe2380040e99e184acfeb7e71c5e940e64283b7 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Mon, 4 Jul 2022 12:10:45 +0200 Subject: [PATCH 36/77] [Structs] Use static_assert --- src/Game/Structs.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 007c39e5..aaa59f7a 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -7070,6 +7070,8 @@ namespace Game entityState_s noDeltaEntities[1024]; }; + static_assert(sizeof(clientStatic_t) == 0xA7AEC); + struct ConDrawInputGlob { char autoCompleteChoice[64]; From a3d92939ad194737403b9803b50c21e45b286a0b Mon Sep 17 00:00:00 2001 From: Diavolo Date: Mon, 4 Jul 2022 21:09:26 +0200 Subject: [PATCH 37/77] [Logger] Filter out coloured strings for stdout --- src/Components/Modules/Ceg.cpp | 2 +- src/Components/Modules/Logger.cpp | 19 ++++++++++++++----- src/Components/Modules/Logger.hpp | 2 +- src/Utils/Hooking.cpp | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index 907a9e20..da0c2432 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -4,7 +4,7 @@ namespace Components { Ceg::Ceg() { - Utils::Hook::Signature signature(0x401000, 0x740000); + Utils::Hook::Signature signature(0x401000, 0x340000); // Generic killer caller. signature.add({ diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 3acd6f93..0c5ee6cf 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -26,25 +26,33 @@ namespace Components void Logger::MessagePrint(const int channel, const std::string& msg) { + std::string out = msg; + + // Filter out coloured strings + if (out[0] == '^' && out[1] != '\0') + { + out = out.substr(2); + } + if (Flags::HasFlag("stdout") || Loader::IsPerformingUnitTests()) { - printf("%s", msg.data()); + printf("%s", out.data()); fflush(stdout); return; } if (!Logger::IsConsoleReady()) { - OutputDebugStringA(msg.data()); + OutputDebugStringA(out.data()); } if (!Game::Sys_IsMainThread()) { - Logger::EnqueueMessage(msg); + Logger::EnqueueMessage(out); } else { - Game::Com_PrintMessage(channel, msg.data(), 0); + Game::Com_PrintMessage(channel, out.data(), 0); } } @@ -54,7 +62,8 @@ namespace Components const auto msg = std::vformat(fmt, args); const auto out = std::format("Debug:\n {}\nFile: {}\nLine: {}\n", msg, loc.file_name(), loc.line()); #else - const auto out = std::vformat(fmt, args) + "\n"; + const auto msg = std::vformat(fmt, args); + const auto out = std::format("^2{}\n", msg); #endif Logger::MessagePrint(Game::CON_CHANNEL_DONT_FILTER, out); diff --git a/src/Components/Modules/Logger.hpp b/src/Components/Modules/Logger.hpp index 88f9f1da..cd281624 100644 --- a/src/Components/Modules/Logger.hpp +++ b/src/Components/Modules/Logger.hpp @@ -16,7 +16,6 @@ namespace Components static void Flush(); - static void MessagePrint(int channel, const std::string& msg); static void PrintInternal(int channel, std::string_view fmt, std::format_args&& args); static void ErrorInternal(Game::errorParm_t error, std::string_view fmt, std::format_args&& args); static void PrintErrorInternal(int channel, std::string_view fmt, std::format_args&& args); @@ -99,6 +98,7 @@ namespace Components static std::vector LoggingAddresses[2]; static void(*PipeCallback)(const std::string&); + static void MessagePrint(int channel, const std::string& msg); static void Frame(); static void G_LogPrintfStub(const char* fmt, ...); static void PrintMessageStub(); diff --git a/src/Utils/Hooking.cpp b/src/Utils/Hooking.cpp index e996e4f7..3b2f4544 100644 --- a/src/Utils/Hooking.cpp +++ b/src/Utils/Hooking.cpp @@ -9,7 +9,7 @@ namespace Utils { if (this->signatures.empty()) return; - char* _start = reinterpret_cast(this->start); + char* _start = static_cast(this->start); unsigned int sigCount = this->signatures.size(); Hook::Signature::Container* containers = this->signatures.data(); From 7841df29825f830a84425f1767fd0dcab66684b3 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Tue, 5 Jul 2022 18:43:24 +0200 Subject: [PATCH 38/77] [Ceg] Remove check in the registry --- src/Components/Modules/Ceg.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index da0c2432..a975c5a8 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -45,5 +45,8 @@ namespace Components Utils::Hook::Set(0x4F4CF0, 0xC3); Utils::Hook::Set(0x432180, 0xC3); Utils::Hook::Set(0x461930, 0xC3); + + // Looking for stuff in the registry + Utils::Hook::Nop(0x4826F8, 5); } } From 60f7c7c2541e072c9872910c99ed177506a6fd00 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Wed, 6 Jul 2022 17:48:40 +0200 Subject: [PATCH 39/77] [Script] Re-work stuff (no hidden changes) *maybe* (Based on a true story and events) --- README.md | 1 - src/Components/Loader.cpp | 4 +- src/Components/Loader.hpp | 4 +- src/Components/Modules/Bots.cpp | 25 +-- src/Components/Modules/Chat.cpp | 1 + src/Components/Modules/ClientCommand.cpp | 1 + src/Components/Modules/Download.cpp | 7 +- src/Components/Modules/GSC/GSC.cpp | 17 ++ src/Components/Modules/GSC/GSC.hpp | 10 + src/Components/Modules/GSC/IO.cpp | 115 +++++++++++ src/Components/Modules/GSC/IO.hpp | 15 ++ src/Components/Modules/{ => GSC}/Script.cpp | 80 +------- src/Components/Modules/{ => GSC}/Script.hpp | 2 - .../Modules/{ => GSC}/ScriptExtension.cpp | 185 ++++-------------- .../Modules/{ => GSC}/ScriptExtension.hpp | 2 - src/Components/Modules/GSC/ScriptStorage.cpp | 101 ++++++++++ src/Components/Modules/GSC/ScriptStorage.hpp | 15 ++ src/Components/Modules/UserInfo.cpp | 1 + src/Components/Modules/Weapon.cpp | 1 + src/Components/Modules/ZoneBuilder.cpp | 12 +- src/Components/Modules/ZoneBuilder.hpp | 1 - src/Utils/CSV.cpp | 82 ++++---- src/Utils/CSV.hpp | 16 +- 23 files changed, 385 insertions(+), 313 deletions(-) create mode 100644 src/Components/Modules/GSC/GSC.cpp create mode 100644 src/Components/Modules/GSC/GSC.hpp create mode 100644 src/Components/Modules/GSC/IO.cpp create mode 100644 src/Components/Modules/GSC/IO.hpp rename src/Components/Modules/{ => GSC}/Script.cpp (89%) rename src/Components/Modules/{ => GSC}/Script.hpp (95%) rename src/Components/Modules/{ => GSC}/ScriptExtension.cpp (60%) rename src/Components/Modules/{ => GSC}/ScriptExtension.hpp (96%) create mode 100644 src/Components/Modules/GSC/ScriptStorage.cpp create mode 100644 src/Components/Modules/GSC/ScriptStorage.hpp diff --git a/README.md b/README.md index 16ebf5cd..aca253ca 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,6 @@ | `-stdout` | Redirect all logging output to the terminal iw4x is started from, or if there is none, creates a new terminal window to write log information in. | | `-console` | Allow the game to display its own separate interactive console window. | | `-dedicated` | Starts the game as a headless dedicated server. | -| `-scriptablehttp` | Enable HTTP related gsc functions. | | `-bigminidumps` | Include all code sections from loaded modules in the dump. | | `-reallybigminidumps` | Include data sections from all loaded modules in the dump. | | `-dump` | Write info of loaded assets to the raw folder as they are being loaded. | diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 004f316e..63975f73 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -49,7 +49,6 @@ namespace Components Loader::Register(new Zones()); Loader::Register(new D3D9Ex()); Loader::Register(new Logger()); - Loader::Register(new Script()); Loader::Register(new Weapon()); Loader::Register(new Window()); Loader::Register(new Command()); @@ -105,7 +104,6 @@ namespace Components Loader::Register(new Elevators()); Loader::Register(new ClientCommand()); Loader::Register(new VisionFile()); - Loader::Register(new ScriptExtension()); Loader::Register(new Branding()); Loader::Register(new Debug()); Loader::Register(new RawMouse()); @@ -115,6 +113,8 @@ namespace Components Loader::Register(new UserInfo()); Loader::Register(new Events()); + Loader::Register(new GSC()); + Loader::Pregame = false; // Make sure preDestroy is called when the game shuts down diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index f3eee146..67bcc26f 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -74,7 +74,6 @@ namespace Components #include "Modules/Toast.hpp" #include "Modules/Zones.hpp" #include "Modules/D3D9Ex.hpp" -#include "Modules/Script.hpp" #include "Modules/Weapon.hpp" #include "Modules/Window.hpp" #include "Modules/Command.hpp" @@ -136,7 +135,6 @@ namespace Components #include "Modules/ClientCommand.hpp" #include "Modules/VisionFile.hpp" #include "Modules/Gamepad.hpp" -#include "Modules/ScriptExtension.hpp" #include "Modules/Branding.hpp" #include "Modules/Debug.hpp" #include "Modules/RawMouse.hpp" @@ -145,3 +143,5 @@ namespace Components #include "Modules/Ceg.hpp" #include "Modules/UserInfo.hpp" #include "Modules/Events.hpp" + +#include "Modules/GSC/GSC.hpp" diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index c1cdaa2d..3278611c 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { @@ -121,24 +122,6 @@ namespace Components Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot(); Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient(); - Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() - { - auto ping = Game::Scr_GetInt(0); - - ping = std::clamp(ping, 0, 999); - - const auto* ent = Game::GetPlayerEntity(entref); - auto* client = Script::GetClient(ent); - - if (Game::SV_IsTestClient(ent->s.number) == 0) - { - Game::Scr_Error("^1SetPing: Can only call on a bot!\n"); - return; - } - - client->ping = static_cast(ping); - }); - Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -201,7 +184,7 @@ namespace Components return; } - for (auto i = 0u; i < std::extent_v; ++i) + for (std::size_t i = 0; i < std::extent_v; ++i) { if (Utils::String::ToLower(&action[1]) != BotActions[i].action) continue; @@ -331,9 +314,9 @@ namespace Components }); // Zero the bot command array - for (auto i = 0u; i < std::extent_v; i++) + for (std::size_t i = 0; i < std::extent_v; ++i) { - g_botai[i] = {0}; + std::memset(&g_botai[i], 0, sizeof(BotMovementInfo)); g_botai[i].weapon = 1; // Prevent the bots from defaulting to the 'none' weapon } diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index ead9e87b..446572e0 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 70f0a452..b0e410e9 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 6cda7c4c..d5a18782 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { @@ -968,9 +969,6 @@ namespace Components Script::AddFunction("HttpGet", [] { - if (!Flags::HasFlag("scriptablehttp")) - return; - const auto* url = Game::Scr_GetString(0); if (url == nullptr) @@ -989,9 +987,6 @@ namespace Components Script::AddFunction("HttpCancel", [] { - if (!Flags::HasFlag("scriptablehttp")) - return; - const auto object = Game::Scr_GetObject(0); for (const auto& download : Download::ScriptDownloads) { diff --git a/src/Components/Modules/GSC/GSC.cpp b/src/Components/Modules/GSC/GSC.cpp new file mode 100644 index 00000000..18de80a4 --- /dev/null +++ b/src/Components/Modules/GSC/GSC.cpp @@ -0,0 +1,17 @@ +#include + +#include "IO.hpp" +#include "Script.hpp" +#include "ScriptExtension.hpp" +#include "ScriptStorage.hpp" + +namespace Components +{ + GSC::GSC() + { + Loader::Register(new IO()); + Loader::Register(new Script()); + Loader::Register(new ScriptExtension()); + Loader::Register(new ScriptStorage()); + } +} diff --git a/src/Components/Modules/GSC/GSC.hpp b/src/Components/Modules/GSC/GSC.hpp new file mode 100644 index 00000000..05d8fc69 --- /dev/null +++ b/src/Components/Modules/GSC/GSC.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace Components +{ + class GSC : public Component + { + public: + GSC(); + }; +} diff --git a/src/Components/Modules/GSC/IO.cpp b/src/Components/Modules/GSC/IO.cpp new file mode 100644 index 00000000..159944eb --- /dev/null +++ b/src/Components/Modules/GSC/IO.cpp @@ -0,0 +1,115 @@ +#include +#include "IO.hpp" +#include "Script.hpp" + +namespace Components +{ + const char* IO::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" }; + + void IO::AddScriptFunctions() + { + Script::AddFunction("FileWrite", [] // gsc: FileWrite(, , ) + { + const auto* path = Game::Scr_GetString(0); + auto* text = Game::Scr_GetString(1); + auto* mode = Game::Scr_GetString(2); + + if (path == nullptr) + { + Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n"); + return; + } + + if (text == nullptr || mode == nullptr) + { + Game::Scr_Error("^1FileWrite: Illegal parameters!\n"); + return; + } + + for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i) + { + if (std::strstr(path, QueryStrings[i]) != nullptr) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileWrite: directory traversal is not allowed!\n"); + return; + } + } + + if (mode != "append"s && mode != "write"s) + { + Logger::Warning(Game::CON_CHANNEL_SCRIPT, "FileWrite: mode not defined or was wrong, defaulting to 'write'\n"); + mode = "write"; + } + + const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path); + + if (mode == "write"s) + { + FileSystem::FileWriter(scriptData).write(text); + } + else if (mode == "append"s) + { + FileSystem::FileWriter(scriptData, true).write(text); + } + }); + + Script::AddFunction("FileRead", [] // gsc: FileRead() + { + const auto* path = Game::Scr_GetString(0); + + if (path == nullptr) + { + Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n"); + return; + } + + for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i) + { + if (std::strstr(path, QueryStrings[i]) != nullptr) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: directory traversal is not allowed!\n"); + return; + } + } + + const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path); + + if (!FileSystem::FileReader(scriptData).exists()) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileRead: file '{}' not found!\n", scriptData); + return; + } + + Game::Scr_AddString(FileSystem::FileReader(scriptData).getBuffer().data()); + }); + + Script::AddFunction("FileExists", [] // gsc: FileExists() + { + const auto* path = Game::Scr_GetString(0); + + if (path == nullptr) + { + Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n"); + return; + } + + for (std::size_t i = 0; i < ARRAYSIZE(QueryStrings); ++i) + { + if (std::strstr(path, QueryStrings[i]) != nullptr) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "FileExists: directory traversal is not allowed!\n"); + return; + } + } + + const auto* scriptData = Utils::String::VA("%s/%s", "scriptdata", path); + + Game::Scr_AddBool(FileSystem::FileReader(scriptData).exists()); + }); + } + + IO::IO() + { + AddScriptFunctions(); + } +} diff --git a/src/Components/Modules/GSC/IO.hpp b/src/Components/Modules/GSC/IO.hpp new file mode 100644 index 00000000..d9a2a758 --- /dev/null +++ b/src/Components/Modules/GSC/IO.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace Components +{ + class IO : public Component + { + public: + IO(); + + private: + static const char* QueryStrings[]; + + static void AddScriptFunctions(); + }; +} diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/GSC/Script.cpp similarity index 89% rename from src/Components/Modules/Script.cpp rename to src/Components/Modules/GSC/Script.cpp index 1d9bd018..6c2e0be3 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -1,4 +1,5 @@ #include +#include "Script.hpp" namespace Components { @@ -7,7 +8,6 @@ namespace Components std::unordered_map Script::CustomScrMethods; std::vector Script::ScriptNameStack; unsigned short Script::FunctionName; - std::unordered_map Script::ScriptStorage; std::unordered_map Script::ScriptBaseProgramNum; int Script::LastFrameTime = -1; @@ -416,11 +416,11 @@ namespace Components unsigned int Script::SetExpFogStub() { - if (Game::Scr_GetNumParam() == 6u) + if (Game::Scr_GetNumParam() == 6) { std::memmove(&Game::scrVmPub->top[-4], &Game::scrVmPub->top[-5], sizeof(Game::VariableValue) * 6); Game::scrVmPub->top += 1; - Game::scrVmPub->top[-6].type = Game::scrParamType_t::VAR_FLOAT; + Game::scrVmPub->top[-6].type = Game::VAR_FLOAT; Game::scrVmPub->top[-6].u.floatValue = 0.0f; ++Game::scrVmPub->outparamcount; @@ -540,7 +540,7 @@ namespace Components { Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) { - if (Game::Scr_GetNumParam() != 2u) + if (Game::Scr_GetNumParam() != 2) { Game::Scr_Error("^1ReplaceFunc: Needs two parameters!\n"); return; @@ -592,78 +592,6 @@ namespace Components } }); - // Script Storage Functions - Script::AddFunction("StorageSet", [] // gsc: StorageSet(, ); - { - const auto* key = Game::Scr_GetString(0); - const auto* value = Game::Scr_GetString(1); - - if (key == nullptr || value == nullptr) - { - Game::Scr_Error("^1StorageSet: Illegal parameters!\n"); - return; - } - - Script::ScriptStorage.insert_or_assign(key, value); - }); - - Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(); - { - const auto* key = Game::Scr_GetString(0); - - if (key == nullptr) - { - Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n"); - return; - } - - if (!Script::ScriptStorage.contains(key)) - { - Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key)); - return; - } - - Script::ScriptStorage.erase(key); - }); - - Script::AddFunction("StorageGet", [] // gsc: StorageGet(); - { - const auto* key = Game::Scr_GetString(0); - - if (key == nullptr) - { - Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n"); - return; - } - - if (!Script::ScriptStorage.contains(key)) - { - Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key)); - return; - } - - const auto& data = Script::ScriptStorage.at(key); - Game::Scr_AddString(data.data()); - }); - - Script::AddFunction("StorageHas", [] // gsc: StorageHas(); - { - const auto* key = Game::Scr_GetString(0); - - if (key == nullptr) - { - Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n"); - return; - } - - Game::Scr_AddBool(Script::ScriptStorage.contains(key)); - }); - - Script::AddFunction("StorageClear", [] // gsc: StorageClear(); - { - Script::ScriptStorage.clear(); - }); - // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); { diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/GSC/Script.hpp similarity index 95% rename from src/Components/Modules/Script.hpp rename to src/Components/Modules/GSC/Script.hpp index 73cbfeab..1adfe31e 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/GSC/Script.hpp @@ -1,5 +1,4 @@ #pragma once -#include namespace Components { @@ -21,7 +20,6 @@ namespace Components static std::unordered_map CustomScrMethods; static std::vector ScriptNameStack; static unsigned short FunctionName; - static std::unordered_map ScriptStorage; static std::unordered_map ScriptBaseProgramNum; static int LastFrameTime; diff --git a/src/Components/Modules/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp similarity index 60% rename from src/Components/Modules/ScriptExtension.cpp rename to src/Components/Modules/GSC/ScriptExtension.cpp index e47bf92c..9dda1b9f 100644 --- a/src/Components/Modules/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -1,9 +1,9 @@ #include +#include "ScriptExtension.hpp" +#include "Script.hpp" namespace Components { - const char* ScriptExtension::QueryStrings[] = { R"(..)", R"(../)", R"(..\)" }; - std::unordered_map ScriptExtension::CustomEntityFields; std::unordered_map ScriptExtension::CustomClientFields; @@ -13,7 +13,7 @@ namespace Components static std::uint16_t fieldOffsetStart = 15; // fields count assert((fieldOffsetStart & Game::ENTFIELD_MASK) == Game::ENTFIELD_ENTITY); - ScriptExtension::CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter}; + CustomEntityFields[fieldOffsetStart] = {name, fieldOffsetStart, type, setter, getter}; ++fieldOffsetStart; } @@ -26,20 +26,20 @@ namespace Components const auto offset = fieldOffsetStart | Game::ENTFIELD_CLIENT; // This is how client field's offset is calculated // Use 'index' in 'array' as map key. It will be used later in Scr_SetObjectFieldStub - ScriptExtension::CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter}; + CustomClientFields[fieldOffsetStart] = {name, offset, type, setter, getter}; ++fieldOffsetStart; } void ScriptExtension::GScr_AddFieldsForEntityStub() { - for (const auto& [offset, field] : ScriptExtension::CustomEntityFields) + for (const auto& [offset, field] : CustomEntityFields) { Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs); } Utils::Hook::Call(0x4A7CF0)(); // GScr_AddFieldsForClient - for (const auto& [offset, field] : ScriptExtension::CustomClientFields) + for (const auto& [offset, field] : CustomClientFields) { Game::Scr_AddClassField(Game::ClassNum::CLASS_NUM_ENTITY, field.name, field.ofs); } @@ -52,8 +52,8 @@ namespace Components { const auto entity_offset = static_cast(offset); - const auto got = ScriptExtension::CustomEntityFields.find(entity_offset); - if (got != ScriptExtension::CustomEntityFields.end()) + const auto got =CustomEntityFields.find(entity_offset); + if (got != CustomEntityFields.end()) { got->second.setter(&Game::g_entities[entnum], offset); return 1; @@ -69,8 +69,8 @@ namespace Components { const auto client_offset = static_cast(offset); - const auto got = ScriptExtension::CustomClientFields.find(client_offset); - if (got != ScriptExtension::CustomClientFields.end()) + const auto got = CustomClientFields.find(client_offset); + if (got != CustomClientFields.end()) { got->second.setter(client, &got->second); return; @@ -89,8 +89,8 @@ namespace Components { const auto client_offset = static_cast(offset & ~Game::ENTFIELD_MASK); - const auto got = ScriptExtension::CustomClientFields.find(client_offset); - if (got != ScriptExtension::CustomClientFields.end()) + const auto got =CustomClientFields.find(client_offset); + if (got != CustomClientFields.end()) { // Game functions probably don't ever need to use the reference to client_fields_s... got->second.getter(Game::g_entities[entnum].client, &got->second); @@ -102,8 +102,8 @@ namespace Components // Regular entity offsets can be searched directly in our custom handler const auto entity_offset = static_cast(offset); - const auto got = ScriptExtension::CustomEntityFields.find(entity_offset); - if (got != ScriptExtension::CustomEntityFields.end()) + const auto got = CustomEntityFields.find(entity_offset); + if (got != CustomEntityFields.end()) { got->second.getter(&Game::g_entities[entnum], offset); return; @@ -115,125 +115,6 @@ namespace Components void ScriptExtension::AddFunctions() { - // File functions - Script::AddFunction("FileWrite", [] // gsc: FileWrite(, , ) - { - const auto* path = Game::Scr_GetString(0); - auto* text = Game::Scr_GetString(1); - auto* mode = Game::Scr_GetString(2); - - if (path == nullptr) - { - Game::Scr_ParamError(0, "^1FileWrite: filepath is not defined!\n"); - return; - } - - if (text == nullptr || mode == nullptr) - { - Game::Scr_Error("^1FileWrite: Illegal parameters!\n"); - return; - } - - for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i) - { - if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr) - { - Logger::Print("^1FileWrite: directory traversal is not allowed!\n"); - return; - } - } - - if (mode != "append"s && mode != "write"s) - { - Logger::Print("^3FileWrite: mode not defined or was wrong, defaulting to 'write'\n"); - mode = "write"; - } - - if (mode == "write"s) - { - FileSystem::FileWriter(path).write(text); - } - else if (mode == "append"s) - { - FileSystem::FileWriter(path, true).write(text); - } - }); - - Script::AddFunction("FileRead", [] // gsc: FileRead() - { - const auto* path = Game::Scr_GetString(0); - - if (path == nullptr) - { - Game::Scr_ParamError(0, "^1FileRead: filepath is not defined!\n"); - return; - } - - for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i) - { - if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr) - { - Logger::Print("^1FileRead: directory traversal is not allowed!\n"); - return; - } - } - - if (!FileSystem::FileReader(path).exists()) - { - Logger::Print("^1FileRead: file not found!\n"); - return; - } - - Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data()); - }); - - Script::AddFunction("FileExists", [] // gsc: FileExists() - { - const auto* path = Game::Scr_GetString(0); - - if (path == nullptr) - { - Game::Scr_ParamError(0, "^1FileExists: filepath is not defined!\n"); - return; - } - - for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i) - { - if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr) - { - Logger::Print("^1FileExists: directory traversal is not allowed!\n"); - return; - } - } - - Game::Scr_AddInt(FileSystem::FileReader(path).exists()); - }); - - Script::AddFunction("FileRemove", [] // gsc: FileRemove() - { - const auto* path = Game::Scr_GetString(0); - - if (path == nullptr) - { - Game::Scr_ParamError(0, "^1FileRemove: filepath is not defined!\n"); - return; - } - - for (auto i = 0u; i < ARRAYSIZE(ScriptExtension::QueryStrings); ++i) - { - if (std::strstr(path, ScriptExtension::QueryStrings[i]) != nullptr) - { - Logger::Print("^1FileRemove: directory traversal is not allowed!\n"); - return; - } - } - - const auto p = std::filesystem::path(path); - const auto& folder = p.parent_path().string(); - const auto& file = p.filename().string(); - Game::Scr_AddInt(FileSystem::DeleteFile(folder, file)); - }); - // Misc functions Script::AddFunction("ToUpper", [] // gsc: ToUpper() { @@ -311,11 +192,11 @@ namespace Components const auto type = Game::Scr_GetType(0); bool result; - if (type == Game::scrParamType_t::VAR_POINTER) + if (type == Game::VAR_POINTER) { const auto ptr_type = Game::Scr_GetPointerType(0); assert(ptr_type >= Game::FIRST_OBJECT); - result = (ptr_type == Game::scrParamType_t::VAR_ARRAY); + result = (ptr_type == Game::VAR_ARRAY); } else { @@ -350,6 +231,18 @@ namespace Components Game::Scr_AddInt(client->ping); }); + + Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() + { + auto ping = Game::Scr_GetInt(0); + + ping = std::clamp(ping, 0, 999); + + const auto* ent = Game::GetPlayerEntity(entref); + auto* client = Script::GetClient(ent); + + client->ping = static_cast(ping); + }); } void ScriptExtension::Scr_TableLookupIStringByRow() @@ -378,7 +271,7 @@ namespace Components void ScriptExtension::AddEntityFields() { - ScriptExtension::AddEntityField("entityflags", Game::fieldtype_t::F_INT, + AddEntityField("entityflags", Game::fieldtype_t::F_INT, [](Game::gentity_s* ent, [[maybe_unused]] int offset) { ent->flags = Game::Scr_GetInt(0); @@ -391,7 +284,7 @@ namespace Components void ScriptExtension::AddClientFields() { - ScriptExtension::AddClientField("clientflags", Game::fieldtype_t::F_INT, + AddClientField("clientflags", Game::fieldtype_t::F_INT, [](Game::gclient_s* pSelf, [[maybe_unused]] const Game::client_fields_s* pField) { pSelf->flags = Game::Scr_GetInt(0); @@ -404,19 +297,19 @@ namespace Components ScriptExtension::ScriptExtension() { - ScriptExtension::AddFunctions(); - ScriptExtension::AddMethods(); - ScriptExtension::AddEntityFields(); - ScriptExtension::AddClientFields(); + AddFunctions(); + AddMethods(); + AddEntityFields(); + AddClientFields(); // Correct builtin function pointer - Utils::Hook::Set(0x79A90C, ScriptExtension::Scr_TableLookupIStringByRow); + Utils::Hook::Set(0x79A90C, Scr_TableLookupIStringByRow); - Utils::Hook(0x4EC721, ScriptExtension::GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity + Utils::Hook(0x4EC721, GScr_AddFieldsForEntityStub, HOOK_CALL).install()->quick(); // GScr_AddFieldsForEntity - 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 + Utils::Hook(0x41BED2, Scr_SetObjectFieldStub, HOOK_CALL).install()->quick(); // SetEntityFieldValue + Utils::Hook(0x5FBF01, Scr_SetClientFieldStub, HOOK_CALL).install()->quick(); // Scr_SetObjectField + Utils::Hook(0x4FF413, 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/ScriptExtension.hpp b/src/Components/Modules/GSC/ScriptExtension.hpp similarity index 96% rename from src/Components/Modules/ScriptExtension.hpp rename to src/Components/Modules/GSC/ScriptExtension.hpp index b4722ea9..cc6cb694 100644 --- a/src/Components/Modules/ScriptExtension.hpp +++ b/src/Components/Modules/GSC/ScriptExtension.hpp @@ -11,8 +11,6 @@ namespace Components static void AddClientField(const char* name, Game::fieldtype_t type, const Game::ScriptCallbackClient& setter, const Game::ScriptCallbackClient& getter); private: - static const char* QueryStrings[]; - static std::unordered_map CustomEntityFields; static std::unordered_map CustomClientFields; diff --git a/src/Components/Modules/GSC/ScriptStorage.cpp b/src/Components/Modules/GSC/ScriptStorage.cpp new file mode 100644 index 00000000..b169d1ea --- /dev/null +++ b/src/Components/Modules/GSC/ScriptStorage.cpp @@ -0,0 +1,101 @@ +#include +#include "ScriptStorage.hpp" +#include "Script.hpp" + +namespace Components +{ + std::unordered_map ScriptStorage::Data; + + void ScriptStorage::AddScriptFunctions() + { + Script::AddFunction("StorageSet", [] // gsc: StorageSet(, ); + { + const auto* key = Game::Scr_GetString(0); + const auto* value = Game::Scr_GetString(1); + + if (key == nullptr || value == nullptr) + { + Game::Scr_Error("^1StorageSet: Illegal parameters!\n"); + return; + } + + Data.insert_or_assign(key, value); + }); + + Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(); + { + const auto* key = Game::Scr_GetString(0); + + if (key == nullptr) + { + Game::Scr_ParamError(0, "^1StorageRemove: Illegal parameter!\n"); + return; + } + + if (!Data.contains(key)) + { + Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key)); + return; + } + + Data.erase(key); + }); + + Script::AddFunction("StorageGet", [] // gsc: StorageGet(); + { + const auto* key = Game::Scr_GetString(0); + + if (key == nullptr) + { + Game::Scr_ParamError(0, "^1StorageGet: Illegal parameter!\n"); + return; + } + + if (!Data.contains(key)) + { + Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key)); + return; + } + + const auto& data = Data.at(key); + Game::Scr_AddString(data.data()); + }); + + Script::AddFunction("StorageHas", [] // gsc: StorageHas(); + { + const auto* key = Game::Scr_GetString(0); + + if (key == nullptr) + { + Game::Scr_ParamError(0, "^1StorageHas: Illegal parameter!\n"); + return; + } + + Game::Scr_AddBool(Data.contains(key)); + }); + + Script::AddFunction("StorageDump", [] // gsc: StorageDump(); + { + if (Data.empty()) + { + Game::Scr_Error("^1StorageDump: ScriptStorage is empty!\n"); + return; + } + + const json11::Json json = Data; + + FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump()); + }); + + Script::AddFunction("StorageClear", [] // gsc: StorageClear(); + { + Data.clear(); + }); + + } + + ScriptStorage::ScriptStorage() + { + AddScriptFunctions(); + } +} diff --git a/src/Components/Modules/GSC/ScriptStorage.hpp b/src/Components/Modules/GSC/ScriptStorage.hpp new file mode 100644 index 00000000..089e1f45 --- /dev/null +++ b/src/Components/Modules/GSC/ScriptStorage.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace Components +{ + class ScriptStorage : public Component + { + public: + ScriptStorage(); + + private: + static std::unordered_map Data; + + static void AddScriptFunctions(); + }; +} diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index 51a87c6c..d4dda49c 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index 58a90e46..ac456da5 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -1,4 +1,5 @@ #include +#include "GSC/Script.hpp" namespace Components { diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index c966f885..2053d21a 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -13,7 +13,6 @@ namespace Components Dvar::Var ZoneBuilder::PreferDiskAssetsDvar; ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0), - // Reserve 100MB by default. // That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory. // That way we can be sure it won't need to reallocate memory. @@ -21,11 +20,8 @@ namespace Components // Well, decompressed maps can get way larger than 100MB, so let's increase that. buffer(0xC800000), zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{ nullptr }, assetDepth(0) - {} - - ZoneBuilder::Zone::Zone() : indexStart(0), externalSize(0), buffer(0xC800000), zoneName("null_zone"), - dataMap(), branding{ nullptr }, assetDepth(0) - {} + { + } ZoneBuilder::Zone::~Zone() { @@ -124,7 +120,7 @@ namespace Components { Logger::Print("Loading required FastFiles...\n"); - for (int i = 0; i < this->dataMap.getRows(); ++i) + for (std::size_t i = 0; i < this->dataMap.getRows(); ++i) { if (this->dataMap.getElementAt(i, 0) == "require") { @@ -149,7 +145,7 @@ namespace Components bool ZoneBuilder::Zone::loadAssets() { - for (int i = 0; i < this->dataMap.getRows(); ++i) + for (std::size_t i = 0; i < this->dataMap.getRows(); ++i) { if (this->dataMap.getElementAt(i, 0) != "require") { diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index 210a028f..40cc8abd 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -32,7 +32,6 @@ namespace Components }; Zone(const std::string& zoneName); - Zone(); ~Zone(); void build(); diff --git a/src/Utils/CSV.cpp b/src/Utils/CSV.cpp index 94370ba9..1985c1bc 100644 --- a/src/Utils/CSV.cpp +++ b/src/Utils/CSV.cpp @@ -2,36 +2,21 @@ namespace Utils { - CSV::CSV(const std::string& file, bool isFile, bool allowComments) + CSV::CSV(const std::string& file, const bool isFile, const bool allowComments) { this->parse(file, isFile, allowComments); } - CSV::~CSV() + std::size_t CSV::getRows() const { - this->dataMap.clear(); + return this->dataMap_.size(); } - int CSV::getRows() + std::size_t CSV::getColumns() const { - return this->dataMap.size(); - } + std::size_t count = 0; - int CSV::getColumns(size_t row) - { - if (this->dataMap.size() > row) - { - return this->dataMap[row].size(); - } - - return 0; - } - - int CSV::getColumns() - { - int count = 0; - - for (int i = 0; i < this->getRows(); ++i) + for (std::size_t i = 0; i < this->getRows(); ++i) { count = std::max(this->getColumns(i), count); } @@ -39,11 +24,21 @@ namespace Utils return count; } - std::string CSV::getElementAt(size_t row, size_t column) + std::size_t CSV::getColumns(const std::size_t row) const { - if (this->dataMap.size() > row) + if (this->dataMap_.size() > row) { - auto _row = this->dataMap[row]; + return this->dataMap_[row].size(); + } + + return 0; + } + + std::string CSV::getElementAt(const std::size_t row, const std::size_t column) const + { + if (this->dataMap_.size() > row) + { + auto& _row = this->dataMap_[row]; if (_row.size() > column) { @@ -51,18 +46,27 @@ namespace Utils } } - return ""; + return {}; } - void CSV::parse(const std::string& file, bool isFile, bool allowComments) + bool CSV::isValid() const + { + return this->valid_; + } + + void CSV::parse(const std::string& file, const bool isFile, const bool allowComments) { std::string buffer; if (isFile) { - if (!Utils::IO::FileExists(file)) return; - buffer = Utils::IO::ReadFile(file); - this->valid = true; + if (!IO::FileExists(file)) + { + return; + } + + buffer = IO::ReadFile(file); + this->valid_ = true; } else { @@ -71,7 +75,7 @@ namespace Utils if (!buffer.empty()) { - auto rows = Utils::String::Split(buffer, '\n'); + const auto rows = String::Split(buffer, '\n'); for (auto& row : rows) { @@ -80,14 +84,14 @@ namespace Utils } } - void CSV::parseRow(const std::string& row, bool allowComments) + void CSV::parseRow(const std::string& row, const bool allowComments) { bool isString = false; std::string element; std::vector _row; char tempStr = 0; - for (unsigned int i = 0; i < row.size(); ++i) + for (std::size_t i = 0; i < row.size(); ++i) { if (row[i] == ',' && !isString) // Flush entry { @@ -95,25 +99,29 @@ namespace Utils element.clear(); continue; } - else if (row[i] == '"') // Start/Terminate string + + if (row[i] == '"') // Start/Terminate string { isString = !isString; continue; } - else if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \" + + if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \" { tempStr = '"'; ++i; } + else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t')) { - //++i; continue; } + else if (!isString && (row[i] == '#' || (row[i] == '/' && (i + 1) < row.size() && row[i + 1] == '/') ) && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful { return; } + else { tempStr = row[i]; @@ -125,11 +133,11 @@ namespace Utils // Push last element _row.push_back(element); - if (_row.size() == 0 || (_row.size() == 1 && !_row[0].size())) // Skip empty rows + if (_row.empty() || (_row.size() == 1 && _row[0].empty())) // Skip empty rows { return; } - this->dataMap.push_back(_row); + this->dataMap_.push_back(_row); } } diff --git a/src/Utils/CSV.hpp b/src/Utils/CSV.hpp index cb1e5567..a72aef72 100644 --- a/src/Utils/CSV.hpp +++ b/src/Utils/CSV.hpp @@ -5,21 +5,19 @@ namespace Utils class CSV { public: - CSV() { } CSV(const std::string& file, bool isFile = true, bool allowComments = true); - ~CSV(); - int getRows(); - int getColumns(); - int getColumns(size_t row); + [[nodiscard]] std::size_t getRows() const; + [[nodiscard]] std::size_t getColumns() const; + [[nodiscard]] std::size_t getColumns(std::size_t row) const; - std::string getElementAt(size_t row, size_t column); + [[nodiscard]] std::string getElementAt(std::size_t row, std::size_t column) const; - bool isValid() { return this->valid; } + [[nodiscard]] bool isValid() const; private: - bool valid = false; - std::vector> dataMap; + bool valid_ = false; + std::vector> dataMap_; void parse(const std::string& file, bool isFile = true, bool allowComments = true); void parseRow(const std::string& row, bool allowComments = true); From 1f7b0ff95ddc7fc168689448c9444c98bafbb3f7 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 7 Jul 2022 15:32:18 +0200 Subject: [PATCH 40/77] [Rawfile] Fix hook --- src/Components/Modules/Gametypes.cpp | 2 +- src/Components/Modules/Localization.cpp | 36 ++++++------ src/Components/Modules/Maps.cpp | 2 +- src/Components/Modules/RawFiles.cpp | 76 ++++++++++++++++++++----- src/Components/Modules/RawFiles.hpp | 4 +- src/Game/Functions.cpp | 4 +- src/Game/Functions.hpp | 7 ++- 7 files changed, 94 insertions(+), 37 deletions(-) diff --git a/src/Components/Modules/Gametypes.cpp b/src/Components/Modules/Gametypes.cpp index d7f567f4..93c580ba 100644 --- a/src/Components/Modules/Gametypes.cpp +++ b/src/Components/Modules/Gametypes.cpp @@ -78,7 +78,7 @@ namespace Components std::string data; for (auto& gametype : gametypes) { - if (Game::LoadModdableRawfile(0, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()))) + if (Game::Scr_AddSourceBuffer(nullptr, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()), nullptr, false)) { data.append(gametype); data.append("\r\n"); diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index 8d43f8ea..8e755938 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -9,7 +9,7 @@ namespace Components void Localization::Set(const std::string& key, const std::string& value) { - std::lock_guard _(Localization::LocalizeMutex); + std::lock_guard _(Localization::LocalizeMutex); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); if (Localization::LocalizeMap.contains(key)) @@ -50,7 +50,7 @@ namespace Components Game::LocalizeEntry* entry = nullptr; { - std::lock_guard _(Localization::LocalizeMutex); + std::lock_guard _(Localization::LocalizeMutex); if (Localization::TempLocalizeMap.contains(key)) { @@ -77,7 +77,7 @@ namespace Components void Localization::SetTemp(const std::string& key, const std::string& value) { - std::lock_guard _(Localization::LocalizeMutex); + std::lock_guard _(Localization::LocalizeMutex); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); if (Localization::TempLocalizeMap.contains(key)) @@ -112,7 +112,7 @@ namespace Components void Localization::ClearTemp() { - std::lock_guard _(Localization::LocalizeMutex); + std::lock_guard _(Localization::LocalizeMutex); Utils::Memory::Allocator* allocator = Utils::Memory::GetAllocator(); for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i) @@ -165,6 +165,7 @@ namespace Components { static const char* staff[] = { + "Snake", "/dev/../", "/dev/console", "/dev/full", @@ -172,8 +173,14 @@ namespace Components "/dev/sr0", "/dev/tty0", "/dev/urandom", - "Snake", - "lsb_release -a" + "Dss0", + "FutureRave", + "H3X1C", + "Homura", + "Laupetin", + "Louvenarde", + "lsb_release -a", + "quaK", }; static const char* contributors[] = @@ -186,17 +193,12 @@ namespace Components "Dasfonia", "Deity", "Dizzy", - "Dss0", - "FutureRave", - "H3X1C", "HardNougat", - "Homura", "INeedGames", + "JTAG", "Killera", "Lithium", - "Louvenarde", "OneFourOne", - "quaK", "RaidMax", "Revo", "RezTech", @@ -204,7 +206,7 @@ namespace Components "Slykuiper", "st0rm", "VVLNT", - "X3RX35" + "X3RX35", }; static const char* specials[] = @@ -219,7 +221,7 @@ namespace Components std::string credits = "^2The IW4x Team:^7\n"; - for (int i = 0; i < ARRAYSIZE(staff); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(staff); ++i) { credits.append(staff[i]); credits.append("\n"); @@ -227,7 +229,7 @@ namespace Components credits.append("\n^3Contributors:^7\n"); - for (int i = 0; i < ARRAYSIZE(contributors); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(contributors); ++i) { credits.append(contributors[i]); credits.append("\n"); @@ -235,7 +237,7 @@ namespace Components credits.append("\n^5Special thanks to:^7\n"); - for (int i = 0; i < ARRAYSIZE(specials); ++i) + for (std::size_t i = 0; i < ARRAYSIZE(specials); ++i) { credits.append(specials[i]); credits.append("\n"); @@ -254,7 +256,7 @@ namespace Components AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [](Game::XAssetType, const std::string& filename) { Game::XAssetHeader header = { nullptr }; - std::lock_guard _(Localization::LocalizeMutex); + std::lock_guard _(Localization::LocalizeMutex); if (Localization::TempLocalizeMap.contains(filename)) { diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index a68966f8..bb093f0e 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -96,7 +96,7 @@ namespace Components const char* Maps::LoadArenaFileStub(const char* name, char* buffer, int size) { - std::string data = Game::LoadModdableRawfile(0, name); + std::string data = Game::Scr_AddSourceBuffer(nullptr, name, nullptr, false); if(Maps::UserMap.isValid()) { diff --git a/src/Components/Modules/RawFiles.cpp b/src/Components/Modules/RawFiles.cpp index 027ac4fb..621e792f 100644 --- a/src/Components/Modules/RawFiles.cpp +++ b/src/Components/Modules/RawFiles.cpp @@ -2,26 +2,74 @@ namespace Components { - void* RawFiles::LoadModdableRawfileFunc(const char* filename) + char* RawFiles::ReadRawFile(const char* filename, char* buf, int size) { - return Game::LoadModdableRawfile(0, filename); + auto fileHandle = 0; + auto fileSize = Game::FS_FOpenFileRead(filename, &fileHandle); + + if (fileHandle != 0) + { + if ((fileSize + 1) <= size) + { + Game::FS_Read(buf, fileSize, fileHandle); + buf[fileSize] = 0; + Game::FS_FCloseFile(fileHandle); + return buf; + } + + Game::FS_FCloseFile(fileHandle); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Ignoring raw file '{}' as it exceeds buffer size {} > {}\n", filename, fileSize, size); + } + + auto* rawfile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_RAWFILE, filename).rawfile; + if (Game::DB_IsXAssetDefault(Game::ASSET_TYPE_RAWFILE, filename)) + { + return nullptr; + } + + Game::DB_GetRawBuffer(rawfile, buf, size); + return buf; + } + + char* RawFiles::GetMenuBuffer(const char* filename) + { + auto fileHandle = 0; + auto fileSize = Game::FS_FOpenFileRead(filename, &fileHandle); + + if (fileHandle != 0) + { + if (fileSize < 0x8000) + { + auto* buffer = static_cast(Game::Z_VirtualAlloc(fileSize + 1)); + Game::FS_Read(buffer, fileSize, fileHandle); + Game::FS_FCloseFile(fileHandle); + return buffer; + } + + Game::FS_FCloseFile(fileHandle); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Menu file too large: {} is {}, max allowed is {}\n", filename, fileSize, 0x8000); + } + + auto* rawfile = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_RAWFILE, filename).rawfile; + if (Game::DB_IsXAssetDefault(Game::ASSET_TYPE_RAWFILE, filename)) + { + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Menu file not found: {}, using default\n", filename); + return nullptr; + } + + auto* buffer = static_cast(Game::Z_VirtualAlloc(rawfile->len + 1)); + Game::DB_GetRawBuffer(rawfile, buffer, rawfile->len + 1); + return buffer; } RawFiles::RawFiles() { - Utils::Hook(0x632155, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x5FA46C, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x5FA4D6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x6321EF, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - //Utils::Hook(0x630A88, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); // Arena parsing, handled by usermap hook - Utils::Hook(0x59A6F8, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x57F1E6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x57ED36, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - //Utils::Hook(0x609832, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - // remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles Utils::Hook::Nop(0x61AB76, 2); + Utils::Hook(0x4DA0D0, ReadRawFile, HOOK_JUMP).install()->quick(); + Utils::Hook(0x631640, GetMenuBuffer, HOOK_JUMP).install()->quick(); + Command::Add("dumpraw", [](Command::Params* params) { if (params->size() < 2) @@ -38,9 +86,9 @@ namespace Components return; } - const char* data = Game::LoadModdableRawfile(0, file.getName().data()); + const char* data = Game::Scr_AddSourceBuffer(nullptr, file.getName().data(), nullptr, false); - if (data) + if (data != nullptr) { Utils::IO::WriteFile("raw/" + file.getName(), data); Logger::Print("File '{}' written to raw!\n", file.getName()); diff --git a/src/Components/Modules/RawFiles.hpp b/src/Components/Modules/RawFiles.hpp index 71411f90..6dca6efe 100644 --- a/src/Components/Modules/RawFiles.hpp +++ b/src/Components/Modules/RawFiles.hpp @@ -7,6 +7,8 @@ namespace Components public: RawFiles(); - static void* LoadModdableRawfileFunc(const char* filename); + private: + static char* ReadRawFile(const char* filename, char* buf, int size); + static char* GetMenuBuffer(const char* filename); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 871095d3..aff54283 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -253,7 +253,7 @@ namespace Game Live_GetPrestige_t Live_GetPrestige = Live_GetPrestige_t(0x430F90); Live_GetXp_t Live_GetXp = Live_GetXp_t(0x404C60); - LoadModdableRawfile_t LoadModdableRawfile = LoadModdableRawfile_t(0x61ABC0); + Scr_AddSourceBuffer_t Scr_AddSourceBuffer = Scr_AddSourceBuffer_t(0x61ABC0); PC_ReadToken_t PC_ReadToken = PC_ReadToken_t(0x4ACCD0); PC_ReadTokenHandle_t PC_ReadTokenHandle = PC_ReadTokenHandle_t(0x4D2060); @@ -463,6 +463,8 @@ namespace Game Vec2Normalize_t Vec2Normalize = Vec2Normalize_t(0x416F70); Vec2NormalizeFast_t Vec2NormalizeFast = Vec2NormalizeFast_t(0x5FC830); + Z_VirtualAlloc_t Z_VirtualAlloc = Z_VirtualAlloc_t(0x4CFBA0); + 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 519841ce..2b0d352b 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -651,8 +651,8 @@ namespace Game typedef int(__cdecl * Live_GetXp_t)(int controllerIndex); extern Live_GetXp_t Live_GetXp; - typedef char* (__cdecl * LoadModdableRawfile_t)(int a1, const char* filename); - extern LoadModdableRawfile_t LoadModdableRawfile; + typedef char*(__cdecl * Scr_AddSourceBuffer_t)(const char* filename, const char* extFilename, const char* codePos, bool archive); + extern Scr_AddSourceBuffer_t Scr_AddSourceBuffer; typedef int(__cdecl * PC_ReadToken_t)(source_t*, token_t*); extern PC_ReadToken_t PC_ReadToken; @@ -1113,6 +1113,9 @@ namespace Game typedef void(__cdecl * Vec2NormalizeFast_t)(float* v); extern Vec2NormalizeFast_t Vec2NormalizeFast; + typedef void*(__cdecl * Z_VirtualAlloc_t)(int size); + extern Z_VirtualAlloc_t Z_VirtualAlloc; + extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; From 0516262fdc784ffa4fbfed1bc03e79812b961d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Jul 2022 17:27:08 +0000 Subject: [PATCH 41/77] Bump deps/protobuf from `6e9e603` to `d6ea7fe` Bumps [deps/protobuf](https://github.com/google/protobuf) from `6e9e603` to `d6ea7fe`. - [Release notes](https://github.com/google/protobuf/releases) - [Commits](https://github.com/google/protobuf/compare/6e9e60367d8744e86856590d8ea0e793c61deeec...d6ea7fe01d0f02fe64cfd3c73943728852b0ffa8) --- updated-dependencies: - dependency-name: deps/protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/protobuf b/deps/protobuf index 6e9e6036..d6ea7fe0 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 6e9e60367d8744e86856590d8ea0e793c61deeec +Subproject commit d6ea7fe01d0f02fe64cfd3c73943728852b0ffa8 From a0c1e4e464d949732f3624fbf72444c36d0775ce Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 8 Jul 2022 18:40:09 +0200 Subject: [PATCH 42/77] [Ceg] Remove very expensive function from Live_Init --- src/Components/Modules/Ceg.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Components/Modules/Ceg.cpp b/src/Components/Modules/Ceg.cpp index a975c5a8..4941656c 100644 --- a/src/Components/Modules/Ceg.cpp +++ b/src/Components/Modules/Ceg.cpp @@ -48,5 +48,8 @@ namespace Components // Looking for stuff in the registry Utils::Hook::Nop(0x4826F8, 5); + + // Live_Init + Utils::Hook::Nop(0x420937, 5); } } From 2a21f754e04b2a518456f67529bd7dbfbfb18288 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 8 Jul 2022 18:42:09 +0200 Subject: [PATCH 43/77] [PlayerName] Fix format --- src/Components/Modules/PlayerName.cpp | 80 +++++++++++++-------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/Components/Modules/PlayerName.cpp b/src/Components/Modules/PlayerName.cpp index 3233c279..74d02168 100644 --- a/src/Components/Modules/PlayerName.cpp +++ b/src/Components/Modules/PlayerName.cpp @@ -2,57 +2,57 @@ namespace Components { - Dvar::Var PlayerName::sv_allowColoredNames; + Dvar::Var PlayerName::sv_allowColoredNames; - void PlayerName::UserInfoCopy(char* buffer, const char* name, const size_t size) - { - if (!sv_allowColoredNames.get()) - { - char nameBuffer[64] = {0}; - TextRenderer::StripColors(name, nameBuffer, sizeof(nameBuffer)); - TextRenderer::StripAllTextIcons(nameBuffer, buffer, size); - } - else - { - TextRenderer::StripAllTextIcons(name, buffer, size); - } + void PlayerName::UserInfoCopy(char* buffer, const char* name, const size_t size) + { + if (!sv_allowColoredNames.get()) + { + char nameBuffer[64] = {0}; + TextRenderer::StripColors(name, nameBuffer, sizeof(nameBuffer)); + TextRenderer::StripAllTextIcons(nameBuffer, buffer, size); + } + else + { + TextRenderer::StripAllTextIcons(name, buffer, size); + } - std::string readablePlayerName(buffer); - readablePlayerName = Utils::String::Trim(readablePlayerName); + std::string readablePlayerName(buffer); + readablePlayerName = Utils::String::Trim(readablePlayerName); - if (readablePlayerName.size() < 3) - { - strncpy(buffer, "Unknown Soldier", size); - } - } + if (readablePlayerName.size() < 3) + { + strncpy(buffer, "Unknown Soldier", size); + } + } - __declspec(naked) void PlayerName::ClientCleanName() - { - __asm - { - mov eax, [esp + 4h] // length + __declspec(naked) void PlayerName::ClientCleanName() + { + __asm + { + mov eax, [esp + 4h] // length - push eax + push eax - push ecx // name - push edx // buffer + push ecx // name + push edx // buffer - call UserInfoCopy + call UserInfoCopy - add esp, 0Ch - retn - } - } + add esp, 0Ch + retn + } + } - char* PlayerName::GetClientName(int localClientNum, int index, char* buf, size_t size) - { - Game::CL_GetClientName(localClientNum, index, buf, size); + char* PlayerName::GetClientName(int localClientNum, int index, char* buf, size_t size) + { + Game::CL_GetClientName(localClientNum, index, buf, size); - // Append clantag to username & remove the colors - strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetUserClantag(index, buf)).data(), size); + // Append clantag to username & remove the colors + strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetUserClantag(index, buf)).data(), size); - return buf; - } + return buf; + } char* PlayerName::CleanStrStub(char* string) { From 25a3b8f4850300b1b841cdffe9c8af73d65794cc Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 9 Jul 2022 23:07:58 +0200 Subject: [PATCH 44/77] [Debug] Com_Assert_f for the memes --- src/Components/Modules/Debug.cpp | 7 +++++++ src/Components/Modules/Debug.hpp | 2 ++ 2 files changed, 9 insertions(+) diff --git a/src/Components/Modules/Debug.cpp b/src/Components/Modules/Debug.cpp index 2821d58c..9a2fc119 100644 --- a/src/Components/Modules/Debug.cpp +++ b/src/Components/Modules/Debug.cpp @@ -213,6 +213,11 @@ namespace Components } } + void Debug::Com_Assert_f() + { + assert(("a", false)); + } + void Debug::CL_InitDebugDvars() { static const char* debugOverlayNames_0[] = @@ -236,5 +241,7 @@ namespace Components // Hook end of CG_DrawDebugOverlays (This is to ensure some checks are done before our hook is executed). Utils::Hook(0x49CB0A, CG_DrawDebugOverlays_Hk, HOOK_JUMP).install()->quick(); + + Utils::Hook::Set(0x60BCEA, Com_Assert_f); } } diff --git a/src/Components/Modules/Debug.hpp b/src/Components/Modules/Debug.hpp index 5f031cfe..a3d9dfcf 100644 --- a/src/Components/Modules/Debug.hpp +++ b/src/Components/Modules/Debug.hpp @@ -31,6 +31,8 @@ namespace Components static void CG_DrawDebugOverlays_Hk(int localClientNum); + static void Com_Assert_f(); + static void CL_InitDebugDvars(); }; } From cb6b46c8e56598262114dc72346b9a292351cbaf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jul 2022 17:23:47 +0000 Subject: [PATCH 45/77] Bump deps/protobuf from `d6ea7fe` to `fb6f8da` Bumps [deps/protobuf](https://github.com/google/protobuf) from `d6ea7fe` to `fb6f8da`. - [Release notes](https://github.com/google/protobuf/releases) - [Commits](https://github.com/google/protobuf/compare/d6ea7fe01d0f02fe64cfd3c73943728852b0ffa8...fb6f8da08b60b6beb5bb360d79dd3feda0147da7) --- updated-dependencies: - dependency-name: deps/protobuf dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/protobuf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/protobuf b/deps/protobuf index d6ea7fe0..fb6f8da0 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit d6ea7fe01d0f02fe64cfd3c73943728852b0ffa8 +Subproject commit fb6f8da08b60b6beb5bb360d79dd3feda0147da7 From e961f70fda4e640d06025ea863ee2500ceb260a9 Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Sat, 16 Jul 2022 13:54:20 +0200 Subject: [PATCH 46/77] =?UTF-8?q?Do=20not=20take=20headicons=20for=20refer?= =?UTF-8?q?ence=20even=20if=20they're=20using=20a=20nice=20tran=E2=80=A6?= =?UTF-8?q?=20(#359)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Do not take headicons for reference even if they're using a nice transparent techset * Update IMaterial.cpp Co-authored-by: rackover Co-authored-by: Edo --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 4e321983..743fbfa5 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -258,7 +258,8 @@ namespace Assets { Game::XAssetHeader header = entry->asset.header; - if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet) + if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet + && !std::string(header.material->info.name).contains("icon")) // Yeah this has a tendency to fuck up a LOT of transparent materials { Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name); From 491d4d3ac3987c0282ab244fe8cd5756ab9e780a Mon Sep 17 00:00:00 2001 From: Edo Date: Sat, 16 Jul 2022 23:24:26 +0200 Subject: [PATCH 47/77] [ClanTag] Add back component + extras (#357) * [ClanTags] Add back component --- src/Components/Loader.cpp | 2 +- src/Components/Modules/Bots.cpp | 2 +- src/Components/Modules/CardTitles.cpp | 30 +- src/Components/Modules/CardTitles.hpp | 26 +- src/Components/Modules/ClanTags.cpp | 295 +++++++++++++ src/Components/Modules/ClanTags.hpp | 48 +++ src/Components/Modules/Clantags.cpp | 100 ----- src/Components/Modules/Clantags.hpp | 20 - src/Components/Modules/ClientCommand.cpp | 8 +- src/Components/Modules/Console.cpp | 2 +- src/Components/Modules/Dedicated.cpp | 3 +- src/Components/Modules/Download.cpp | 2 +- src/Components/Modules/Events.cpp | 16 + src/Components/Modules/Events.hpp | 4 + src/Components/Modules/Menus.cpp | 1 + src/Components/Modules/Party.cpp | 2 +- src/Components/Modules/PlayerName.cpp | 10 +- src/Components/Modules/PlayerName.hpp | 3 +- src/Components/Modules/ServerCommands.cpp | 1 + src/Components/Modules/ServerInfo.cpp | 2 +- src/Components/Modules/UserInfo.cpp | 26 +- src/Game/Functions.cpp | 22 +- src/Game/Functions.hpp | 41 +- src/Game/Structs.hpp | 490 ++++++++++++++++++++-- 24 files changed, 943 insertions(+), 213 deletions(-) create mode 100644 src/Components/Modules/ClanTags.cpp create mode 100644 src/Components/Modules/ClanTags.hpp delete mode 100644 src/Components/Modules/Clantags.cpp delete mode 100644 src/Components/Modules/Clantags.hpp diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 63975f73..c1163385 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -61,7 +61,7 @@ namespace Components Loader::Register(new Network()); Loader::Register(new Session()); Loader::Register(new Theatre()); - //Loader::Register(new ClanTags()); + Loader::Register(new ClanTags()); Loader::Register(new Download()); Loader::Register(new Playlist()); Loader::Register(new RawFiles()); diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 3278611c..72c3836b 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -366,7 +366,7 @@ namespace Components // In case a loaded mod didn't call "BotStop" before the VM shutdown Events::OnVMShutdown([] { - for (std::size_t i = 0; i < std::extent_v; i++) + for (std::size_t i = 0; i < std::extent_v; ++i) { g_botai[i].active = false; } diff --git a/src/Components/Modules/CardTitles.cpp b/src/Components/Modules/CardTitles.cpp index 78e7a2f9..7f5d6164 100644 --- a/src/Components/Modules/CardTitles.cpp +++ b/src/Components/Modules/CardTitles.cpp @@ -3,14 +3,14 @@ namespace Components { std::string CardTitles::CustomTitles[18]; - Dvar::Var CardTitles::CustomTitleDvar; + Dvar::Var CardTitles::CustomTitle; CClient* CardTitles::GetClientByIndex(std::uint32_t index) { return &reinterpret_cast(0x8E77B0)[index]; } - std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s* data) + std::int32_t CardTitles::GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data) { std::int32_t returnResult = lookupResult; @@ -22,12 +22,12 @@ namespace Components } else { - for (auto clientNum = 0; clientNum < 18; clientNum++) + for (std::size_t clientNum = 0; clientNum < Game::MAX_CLIENTS; ++clientNum) { CClient* c = GetClientByIndex(clientNum); if (c != nullptr) { - if (!strcmp(data->name, c->Name)) + if (!std::strcmp(data->name, c->Name)) { // Since a 4 byte integer is overkill for a row num: We can use it to store the customprefix + clientNum and use a 2 byte integer for the row number returnResult += 0xFF000000; @@ -62,7 +62,6 @@ namespace Components mov [ebx + 4], eax pop ebx - push 62EB2Ch retn } } @@ -83,10 +82,10 @@ namespace Components { if (prefix == 0xFE) { - if (!CardTitles::CustomTitleDvar.get().empty()) + if (!CardTitles::CustomTitle.get().empty()) { // 0xFF in front of the title to skip localization. Or else it will wait for a couple of seconds for the asset of type localize - const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitleDvar.get()); + const char* title = Utils::String::VA("\x15%s", CardTitles::CustomTitle.get()); // prepare return value operand->internals.stringVal.string = title; @@ -157,20 +156,20 @@ namespace Components { std::string list; - for (int i = 0; i < 18; i++) + for (std::size_t i = 0; i < Game::MAX_CLIENTS; i++) { char playerTitle[18]; - if (Game::svs_clients[i].state >= 3) + if (Game::svs_clients[i].state >= Game::CS_CONNECTED) { - strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "customTitle"), 17); + strncpy_s(playerTitle, Game::Info_ValueForKey(Game::svs_clients[i].userinfo, "customTitle"), _TRUNCATE); } else { - memset(playerTitle, 0, 18); + playerTitle[0] = '\0'; } - list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).c_str(), playerTitle)); + list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), playerTitle)); } const auto* command = Utils::String::VA("%c customTitles \"%s\"", 21, list.data()); @@ -179,7 +178,7 @@ namespace Components void CardTitles::ParseCustomTitles(const char* msg) { - for (int i = 0; i < 18; ++i) + for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) { const char* playerTitle = Game::Info_ValueForKey(msg, std::to_string(i).c_str()); @@ -192,7 +191,7 @@ namespace Components { Scheduler::Once([] { - CardTitles::CustomTitleDvar = Dvar::Register("customtitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title"); + CardTitles::CustomTitle = Dvar::Register("customTitle", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, "Custom card title"); }, Scheduler::Pipeline::MAIN); ServerCommands::OnCommand(21, [](Command::Params* params) @@ -215,8 +214,5 @@ namespace Components // Table lookup stuff Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick(); Utils::Hook::Nop(0x62DCC6, 1); - - // This is placed here in case the anticheat has been disabled! - // This checks specifically for launching the process suspended to inject a dll } } diff --git a/src/Components/Modules/CardTitles.hpp b/src/Components/Modules/CardTitles.hpp index 6f02b47a..7dcf5ce8 100644 --- a/src/Components/Modules/CardTitles.hpp +++ b/src/Components/Modules/CardTitles.hpp @@ -12,18 +12,6 @@ namespace Components std::int32_t tableColumn; }; - struct playercarddata_s - { - std::uint32_t padding; - std::uint32_t playercardNumber; - std::uint32_t unknown; - std::uint32_t unknown2; - std::uint32_t level; //Level is counted from 0 -> Value 69 is Level 70 - std::uint32_t prestige; - std::uint32_t padding2; - char name[40]; - }; - struct CClient { std::uint32_t IsValid; // 0x0000 @@ -54,21 +42,21 @@ namespace Components class CardTitles : public Component { public: - static Dvar::Var CustomTitleDvar; + AssertOffset(Game::PlayerCardData, Game::PlayerCardData::name, 0x1C); + + static Dvar::Var CustomTitle; static std::string CustomTitles[18]; static void SendCustomTitlesToClients(); - static void ParseCustomTitles(const char * msg); + static void ParseCustomTitles(const char* msg); CardTitles(); private: - static CClient * GetClientByIndex(std::uint32_t index); - static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, playercarddata_s * data); + static CClient* GetClientByIndex(std::uint32_t index); + static std::int32_t GetPlayerCardClientInfo(std::int32_t lookupResult, Game::PlayerCardData* data); static void GetPlayerCardClientInfoStub(); - static const char* TableLookupByRowHook(Game::Operand * operand, tablelookuprequest_s * request); + static const char* TableLookupByRowHook(Game::Operand* operand, tablelookuprequest_s* request); static void TableLookupByRowHookStub(); - - }; } diff --git a/src/Components/Modules/ClanTags.cpp b/src/Components/Modules/ClanTags.cpp new file mode 100644 index 00000000..89086c11 --- /dev/null +++ b/src/Components/Modules/ClanTags.cpp @@ -0,0 +1,295 @@ +#include + +namespace Components +{ + Game::dvar_t* ClanTags::ClanName; + + // bgs_t and clientState_s do not have this + char ClanTags::ClientState[Game::MAX_CLIENTS][5]; + + const char* ClanTags::GetClanTagWithName(int clientNum, const char* playerName) + { + assert(static_cast(clientNum) < Game::MAX_CLIENTS); + + if (ClientState[clientNum][0] == '\0') + { + return playerName; + } + + return Utils::String::VA("[%s]%s", ClientState[clientNum], playerName); + } + + void ClanTags::SendClanTagsToClients() + { + std::string list; + + for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) + { + list.append(std::format("\\{}\\{}", std::to_string(i), ClientState[i])); + } + + const auto* command = Utils::String::VA("%c clanNames \"%s\"", 22, list.data()); + Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command); + } + + void ClanTags::ParseClanTags(const char* infoString) + { + for (std::size_t i = 0; i < Game::MAX_CLIENTS; ++i) + { + const auto* clanTag = Game::Info_ValueForKey(infoString, std::to_string(i).data()); + + if (clanTag[0] == '\0') + { + ClientState[i][0] = '\0'; + } + else + { + strncpy_s(ClientState[i], clanTag, _TRUNCATE); + } + } + } + + int ClanTags::CL_FilterChar(unsigned char input) + { + if (input == '^') + { + return ' '; + } + if (input < ' ') + { + return -1; + } + + if (input == 188 || input == 189) + { + return -1; + } + + return input; + } + + void ClanTags::CL_SanitizeClanName() + { + char saneNameBuf[5]; + std::memset(saneNameBuf, 0, sizeof(saneNameBuf)); + + auto* saneName = saneNameBuf; + const auto* currentName = Game::Dvar_GetString("clanName"); + if (currentName) + { + auto nameLen = std::strlen(currentName); + for (std::size_t i = 0; i < nameLen; ++i) + { + auto curChar = CL_FilterChar(static_cast(currentName[i])); + if (curChar > 0) + { + *saneName++ = curChar & 0xFF; + } + } + + Game::Dvar_SetStringByName("clanName", saneNameBuf); + } + } + + char* ClanTags::GamerProfile_GetClanName(int controllerIndex) + { + assert(static_cast(controllerIndex) < Game::MAX_LOCAL_CLIENTS); + + CL_SanitizeClanName(); + strncpy_s(Game::gamerSettings[0].exeConfig.clanPrefix, Game::Dvar_GetString("clanName"), _TRUNCATE); + + return Game::gamerSettings[controllerIndex].exeConfig.clanPrefix; + } + + void ClanTags::Dvar_InfoString_Stub(char* s, const char* key, const char* value) + { + Utils::Hook::Call(0x4AE560)(s, key, value); // Info_SetValueForKey + + // Set 'clanAbbrev' in the info string + Utils::Hook::Call(0x4AE560)(s, "clanAbbrev", GamerProfile_GetClanName(0)); // Info_SetValueForKey + } + + void ClanTags::ClientUserinfoChanged(const char* s, int clientNum) + { + assert(static_cast(clientNum) < Game::MAX_CLIENTS); + + auto* clanAbbrev = Game::Info_ValueForKey(s, "clanAbbrev"); + + if (clanAbbrev[0] == '\0') + { + ClientState[clientNum][0] = '\0'; + } + else + { + strncpy_s(ClientState[clientNum], clanAbbrev, _TRUNCATE); + } + + SendClanTagsToClients(); + } + + void __declspec(naked) ClanTags::ClientUserinfoChanged_Stub() + { + __asm + { + pushad + + push [esp + 0x20 + 0x824] // clientNum + push ecx // s + call ClientUserinfoChanged + add esp, 0x8 + + popad + + push 0x445334 // Return address + push 0x47C820 // Info_ValueForKey + // Jump to Info_ValueForKey & add return address + retn + } + } + + void __declspec(naked) ClanTags::DrawPlayerNameOnScoreboard() + { + __asm + { + push eax + pushad + + push edi + push [ebp] + + call GetClanTagWithName + add esp, 0x8 + + mov [esp + 0x20], eax + + popad + pop edi + + push 0x591247 // Return address + push 0x5909E0 // DrawListString + retn + } + } + + // s1 is always an empty string + int ClanTags::PartyClient_Frame_Stub(const char* s0, [[maybe_unused]] const char* s1) + { + return Utils::Hook::Call(0x4B0100)(s0, GamerProfile_GetClanName(0)); // I_strcmp + } + + // clanAbbrev is always an empty string + void ClanTags::Party_UpdateClanName_Stub(Game::PartyData* party, [[maybe_unused]] const char* clanAbbrev) + { + Utils::Hook::Call(0x4B3B10)(party, GamerProfile_GetClanName(0)); // Party_UpdateClanName + } + + void ClanTags::PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, const int clientNum) + { + strncpy_s(data->clanAbbrev, ClientState[clientNum], _TRUNCATE); + } + + void __declspec(naked) ClanTags::PlayerCards_SetCachedPlayerData_Stub() + { + static DWORD func = 0x4D6F80; // I_strncpyz + + __asm + { + call func + add esp, 0xC + + mov byte ptr [esi + 0x3C], 0x0 + + // Copy the clanName + push [esp + 0xC] // clientNum + push esi // g_PlayerCardCache + call PlayerCards_SetCachedPlayerData + add esp, 0x8 + + // Exit function + pop esi + ret + } + } + + Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForClient_Stub(const unsigned int clientIndex) + { + auto* result = Utils::Hook::Call(0x46C0F0)(clientIndex); + strncpy_s(result->clanAbbrev, ClientState[clientIndex], _TRUNCATE); + + return result; + } + + Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex) + { + auto* result = Utils::Hook::Call(0x463B90)(controllerIndex); + strncpy_s(result->clanAbbrev, GamerProfile_GetClanName(static_cast(controllerIndex)), _TRUNCATE); // controllerIndex should always be 0 + + return result; + } + + Game::PlayerCardData* ClanTags::PlayerCards_GetPartyMemberData(const int localClientNum, const Game::PlayerCardClientLookupType lookupType, const unsigned int memberIndex) + { + auto* result = Utils::Hook::Call(0x4A4A90)(localClientNum, lookupType, memberIndex); + strncpy_s(result->clanAbbrev, ClientState[memberIndex], _TRUNCATE); // controllerIndex should always be 0 + + return result; + } + + ClanTags::ClanTags() + { + Scheduler::Once([] + { + ClanName = Game::Dvar_RegisterString("clanName", "", Game::DVAR_ARCHIVE, + "Your clan abbreviation"); + }, Scheduler::Pipeline::MAIN); + + std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][5])); + + ServerCommands::OnCommand(22, [](Command::Params* params) + { + if (std::strcmp(params->get(1), "clanNames") == 0) + { + if (params->size() == 3) + { + ParseClanTags(params->get(2)); + return true; + } + } + + return false; + }); + + Utils::Hook(0x430B00, Dvar_InfoString_Stub, HOOK_CALL).install()->quick(); + + Utils::Hook(0x44532F, ClientUserinfoChanged_Stub, HOOK_JUMP).install()->quick(); + + // clanName before playerName + Utils::Hook(0x591242, DrawPlayerNameOnScoreboard, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x49765B, PartyClient_Frame_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x49767E, Party_UpdateClanName_Stub, HOOK_CALL).install()->quick(); + + // clanName in the PlayerCard (GetPlayerCardClientData) + Utils::Hook(0x458DF4, PlayerCards_SetCachedPlayerData_Stub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x62EAB6, PlayerCards_GetLiveProfileDataForClient_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x62EAC3, PlayerCards_GetLiveProfileDataForController_Stub, HOOK_CALL).install()->quick(); + Utils::Hook(0x62EAE8, PlayerCards_GetPartyMemberData, HOOK_CALL).install()->quick(); + + // clanName in CG_Obituary + Utils::Hook(0x586DD6, PlayerName::GetClientName, HOOK_CALL).install()->quick(); + Utils::Hook(0x586E2A, PlayerName::GetClientName, HOOK_CALL).install()->quick(); + + Command::Add("statGet", [](Command::Params* params) + { + if (params->size() < 2) + { + Logger::PrintError(Game::CON_CHANNEL_SERVER, "statget usage: statget \n"); + return; + } + + const auto index = std::atoi(params->get(1)); + const auto stat = Game::LiveStorage_GetStat(0, index); + Logger::Print(Game::CON_CHANNEL_SYSTEM, "Stat {}: {}\n", index, stat); + }); + } +} diff --git a/src/Components/Modules/ClanTags.hpp b/src/Components/Modules/ClanTags.hpp new file mode 100644 index 00000000..dc1c7952 --- /dev/null +++ b/src/Components/Modules/ClanTags.hpp @@ -0,0 +1,48 @@ +#pragma once + +namespace Components +{ + class ClanTags : public Component + { + public: + ClanTags(); + + static const char* GetClanTagWithName(int clientNum, const char* playerName); + + static void SendClanTagsToClients(); + + static void CL_SanitizeClanName(); + + private: + static Game::dvar_t* ClanName; + + static const char* dvarNameList[]; + + static char ClientState[Game::MAX_CLIENTS][5]; + + static void ParseClanTags(const char* infoString); + + static int CL_FilterChar(unsigned char input); + + static char* GamerProfile_GetClanName(int controllerIndex); + + static void Dvar_InfoString_Stub(char* s, const char* key, const char* value); + + static void SetCachedPlayerData(int clientNum); + + static void ClientUserinfoChanged(const char* s, int clientNum); + static void ClientUserinfoChanged_Stub(); + + static void DrawPlayerNameOnScoreboard(); + + static int PartyClient_Frame_Stub(const char* s0, const char* s1); + static void Party_UpdateClanName_Stub(Game::PartyData* party, const char* clanAbbrev); + + static void PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, int clientNum); + static void PlayerCards_SetCachedPlayerData_Stub(); + + static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForClient_Stub(unsigned int clientIndex); + static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForController_Stub(unsigned int controllerIndex); + static Game::PlayerCardData* PlayerCards_GetPartyMemberData(int localClientNum, Game::PlayerCardClientLookupType lookupType, unsigned int memberIndex); + }; +} diff --git a/src/Components/Modules/Clantags.cpp b/src/Components/Modules/Clantags.cpp deleted file mode 100644 index eac6a916..00000000 --- a/src/Components/Modules/Clantags.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include - -namespace Components -{ - std::string ClanTags::Tags[18]; - - void ClanTags::ParseClantags(const char* infoString) - { - for (int i = 0; i < 18; i++) - { - const char* clantag = Game::Info_ValueForKey(infoString, std::to_string(i).data()); - - if (clantag) ClanTags::Tags[i] = clantag; - else ClanTags::Tags[i].clear(); - } - } - - void ClanTags::SendClantagsToClients() - { - std::string list; - - for (int i = 0; i < 18; ++i) - { - char clantag[5] = { 0 }; - - if (Game::svs_clients[i].state >= 3) - { - strncpy_s(clantag, Game::Info_ValueForKey(Game::svs_clients[i].connectInfoString, "clantag"), 4); - } - - list.append(Utils::String::VA("\\%s\\%s", std::to_string(i).data(), clantag)); - } - - std::string command = Utils::String::VA("%c clantags \"%s\"", 22, list.data()); - Game::SV_GameSendServerCommand(-1, Game::SV_CMD_CAN_IGNORE, command.data()); - } - - const char* ClanTags::GetUserClantag(std::uint32_t /*clientnum*/, const char* playername) - { -#if 0 - if (ClanTags::Tags[clientnum].empty()) return playername; - return Utils::String::VA("[%s] %s", ClanTags::Tags[clientnum].data(), playername); -#else - return playername; -#endif - - } - - __declspec(naked) void ClanTags::DrawPlayerNameOnScoreboard() - { - __asm - { - push eax - pushad - - push edi - push [ebp] - - call ClanTags::GetUserClantag - add esp, 8 - - mov [esp + 20h], eax - - popad - pop edi - - push 591247h // Return address - push 5909E0h // Draw string func - retn - } - } - - ClanTags::ClanTags() - { - // Create clantag dvar - Scheduler::Once([] - { - Dvar::Register("clantag", "", Game::DVAR_USERINFO | Game::DVAR_ARCHIVE, - "If set, your clantag will be shown on the scoreboard."); - }, Scheduler::Pipeline::MAIN); - - // Servercommand hook - ServerCommands::OnCommand(22, [](Command::Params* params) - { - if (params->get(1) == "clantags"s && !Dedicated::IsEnabled()) - { - if (params->size() == 3) - { - ClanTags::ParseClantags(params->get(2)); - return true; - } - } - - return false; - }); - - // Draw clantag before playername - Utils::Hook(0x591242, ClanTags::DrawPlayerNameOnScoreboard).install()->quick(); - } -} diff --git a/src/Components/Modules/Clantags.hpp b/src/Components/Modules/Clantags.hpp deleted file mode 100644 index ea7bca39..00000000 --- a/src/Components/Modules/Clantags.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -namespace Components -{ - class ClanTags : public Component - { - public: - static void ParseClantags(const char * infoString); - static void SendClantagsToClients(); - static const char* GetUserClantag(std::uint32_t clientnum, const char * playername); - - ClanTags(); - - private: - static std::string Tags[18]; - - static void DrawPlayerNameOnScoreboard(); - - }; -} diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index b0e410e9..19057580 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -334,17 +334,17 @@ namespace Components ClientCommand::Add("kill", []([[maybe_unused]] Game::gentity_s* ent, [[maybe_unused]] Command::ServerParams* params) { assert(ent->client != nullptr); - assert(ent->client->connected != Game::clientConnected_t::CON_DISCONNECTED); + assert(ent->client->sess.connected != Game::CON_DISCONNECTED); - if (ent->client->sessionState != Game::sessionState_t::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent)) + if (ent->client->sess.sessionState != Game::SESS_STATE_PLAYING || !ClientCommand::CheatsOk(ent)) return; Scheduler::Once([ent] { - ent->flags &= ~(Game::entityFlag::FL_GODMODE | Game::entityFlag::FL_DEMI_GODMODE); + ent->flags &= ~(Game::FL_GODMODE | Game::FL_DEMI_GODMODE); ent->health = 0; ent->client->ps.stats[0] = 0; - Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::hitLocation_t::HITLOC_NONE, 0); + Game::player_die(ent, ent, ent, 100000, 12, 0, nullptr, Game::HITLOC_NONE, 0); }, Scheduler::Pipeline::SERVER); }); } diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index 349feb90..d7d18992 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -68,7 +68,7 @@ namespace Components { maxclientCount = Dvar::Var("party_maxplayers").get(); //maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); } wclear(Console::InfoWindow); diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 5e475633..436a13a4 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -87,7 +87,7 @@ namespace Components { list.append(Utils::String::VA(" %llX", Game::svs_clients[i].steamID)); - Utils::InfoString info(Game::svs_clients[i].connectInfoString); + Utils::InfoString info(Game::svs_clients[i].userinfo); list.append(Utils::String::VA(" %llX", strtoull(info.get("realsteamId").data(), nullptr, 16))); } else @@ -294,7 +294,6 @@ namespace Components Scheduler::Loop([] { CardTitles::SendCustomTitlesToClients(); - //Clantags::SendClantagsToClients(); }, Scheduler::Pipeline::SERVER, 10s); // Heartbeats diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 21e4444c..9e3b1d5b 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -760,7 +760,7 @@ namespace Components else { // Score and ping are irrelevant - const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); + const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); if (!namePtr || !namePtr[0]) continue; playerInfo["name"] = namePtr; diff --git a/src/Components/Modules/Events.cpp b/src/Components/Modules/Events.cpp index b73ef3a1..e03522ec 100644 --- a/src/Components/Modules/Events.cpp +++ b/src/Components/Modules/Events.cpp @@ -5,6 +5,7 @@ namespace Components Utils::Signal Events::ClientDisconnectSignal; Utils::Signal Events::SteamDisconnectSignal; Utils::Signal Events::ShutdownSystemSignal; + Utils::Signal Events::ClientInitSignal; Utils::Signal Events::ServerInitSignal; void Events::OnClientDisconnect(const Utils::Slot& callback) @@ -22,6 +23,11 @@ namespace Components ShutdownSystemSignal.connect(callback); } + void Events::OnClientInit(const Utils::Slot& callback) + { + ClientInitSignal.connect(callback); + } + void Events::OnSVInit(const Utils::Slot& callback) { ServerInitSignal.connect(callback); @@ -52,6 +58,14 @@ namespace Components Utils::Hook::Call(0x421EE0)(sys); // Scr_ShutdownSystem } + void Events::CL_InitOnceForAllClients_HK() + { + ClientInitSignal(); + ClientInitSignal.clear(); + + Utils::Hook::Call(0x404CA0)(); // CL_InitOnceForAllClients + } + void Events::SV_Init_Hk() { ServerInitSignal(); @@ -69,6 +83,8 @@ namespace Components Utils::Hook(0x47548B, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_LoadGame Utils::Hook(0x4D06BA, Scr_ShutdownSystem_Hk, HOOK_CALL).install()->quick(); // G_ShutdownGame + Utils::Hook(0x60BE5B, CL_InitOnceForAllClients_HK, HOOK_CALL).install()->quick(); // Com_Init_Try_Block_Function + Utils::Hook(0x4D3665, SV_Init_Hk, HOOK_CALL).install()->quick(); // SV_Init } } diff --git a/src/Components/Modules/Events.hpp b/src/Components/Modules/Events.hpp index 08a3675a..a47f6794 100644 --- a/src/Components/Modules/Events.hpp +++ b/src/Components/Modules/Events.hpp @@ -18,6 +18,8 @@ namespace Components static void OnVMShutdown(const Utils::Slot& callback); + static void OnClientInit(const Utils::Slot& callback); + // Client & Server (triggered once) static void OnSVInit(const Utils::Slot& callback); @@ -25,11 +27,13 @@ namespace Components static Utils::Signal ClientDisconnectSignal; static Utils::Signal SteamDisconnectSignal; static Utils::Signal ShutdownSystemSignal; + static Utils::Signal ClientInitSignal; static Utils::Signal ServerInitSignal; static void ClientDisconnect_Hk(int clientNum); static void SteamDisconnect_Hk(); static void Scr_ShutdownSystem_Hk(unsigned char sys); + static void CL_InitOnceForAllClients_HK(); static void SV_Init_Hk(); }; } diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index 2d316acf..89b9c89f 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -859,6 +859,7 @@ namespace Components Menus::Add("ui_mp/iw4x_credits.menu"); Menus::Add("ui_mp/resetclass.menu"); Menus::Add("ui_mp/popup_customtitle.menu"); + Menus::Add("ui_mp/popup_customclan.menu"); } Menus::~Menus() diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index 4a0a5524..d7b7f2cc 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -332,7 +332,7 @@ namespace Components else { maxclientCount = Dvar::Var("party_maxplayers").get(); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); } Utils::InfoString info; diff --git a/src/Components/Modules/PlayerName.cpp b/src/Components/Modules/PlayerName.cpp index 74d02168..11ca774d 100644 --- a/src/Components/Modules/PlayerName.cpp +++ b/src/Components/Modules/PlayerName.cpp @@ -44,14 +44,14 @@ namespace Components } } - char* PlayerName::GetClientName(int localClientNum, int index, char* buf, size_t size) + int PlayerName::GetClientName(int localClientNum, int index, char* buf, int size) { - Game::CL_GetClientName(localClientNum, index, buf, size); + const auto result = Game::CL_GetClientName(localClientNum, index, buf, size); - // Append clantag to username & remove the colors - strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetUserClantag(index, buf)).data(), size); + // Prepend clanName to username & remove the colors + strncpy_s(buf, size, TextRenderer::StripColors(ClanTags::GetClanTagWithName(index, buf)).data(), size); - return buf; + return result; } char* PlayerName::CleanStrStub(char* string) diff --git a/src/Components/Modules/PlayerName.hpp b/src/Components/Modules/PlayerName.hpp index b1beaaa7..ccf61ff4 100644 --- a/src/Components/Modules/PlayerName.hpp +++ b/src/Components/Modules/PlayerName.hpp @@ -9,6 +9,8 @@ namespace Components static void UserInfoCopy(char* buffer, const char* name, size_t size); + static int GetClientName(int localClientNum, int index, char* buf, int size); + private: static Dvar::Var sv_allowColoredNames; // Message used when kicking players @@ -16,7 +18,6 @@ namespace Components static char* CleanStrStub(char* string); static void ClientCleanName(); - static char* GetClientName(int localClientNum, int index, char* buf, size_t size); static bool CopyClientNameCheck(char* dest, const char* source, int size); static void SV_UserinfoChangedStub(); diff --git a/src/Components/Modules/ServerCommands.cpp b/src/Components/Modules/ServerCommands.cpp index 8c67c49b..352e0154 100644 --- a/src/Components/Modules/ServerCommands.cpp +++ b/src/Components/Modules/ServerCommands.cpp @@ -64,5 +64,6 @@ namespace Components { // Server command receive hook Utils::Hook(0x59449F, ServerCommands::CG_DeployServerCommand_Stub).install()->quick(); + Utils::Hook::Nop(0x5944A4, 6); } } diff --git a/src/Components/Modules/ServerInfo.cpp b/src/Components/Modules/ServerInfo.cpp index 685ca7cd..45f11e40 100644 --- a/src/Components/Modules/ServerInfo.cpp +++ b/src/Components/Modules/ServerInfo.cpp @@ -217,7 +217,7 @@ namespace Components else { // Score and ping are irrelevant - const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); + const auto* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); if (!namePtr || !namePtr[0]) continue; name = namePtr; diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index d4dda49c..87a15245 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -63,10 +63,34 @@ namespace Components { const auto* ent = Game::GetPlayerEntity(entref); - Logger::Debug("Resetting name of {} ", ent->s.number); + Logger::Debug("Resetting name of {}", ent->s.number); UserInfoOverrides[ent->s.number].erase("name"); Game::ClientUserinfoChanged(ent->s.number); }); + + Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self setClanTag() + { + const auto* ent = Game::GetPlayerEntity(entref); + const auto* clanName = Game::Scr_GetString(0); + + if (clanName == nullptr) + { + Game::Scr_ParamError(0, "^1SetClanTag: Illegal parameter!\n"); + } + + Logger::Debug("Setting clanName of {} to {}", ent->s.number, clanName); + UserInfoOverrides[ent->s.number]["clanAbbrev"] = clanName; + Game::ClientUserinfoChanged(ent->s.number); + }); + + Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self ResetClanTag() + { + const auto* ent = Game::GetPlayerEntity(entref); + + Logger::Debug("Resetting clanName of {}", ent->s.number); + UserInfoOverrides[ent->s.number].erase("clanAbbrev"); + Game::ClientUserinfoChanged(ent->s.number); + }); } UserInfo::UserInfo() diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index aff54283..5e4b2893 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -66,6 +66,8 @@ namespace Game CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f = CL_SelectStringTableEntryInDvar_f_t(0x4A4560); CL_DrawStretchPic_t CL_DrawStretchPic = CL_DrawStretchPic_t(0x412490); CL_ConsoleFixPosition_t CL_ConsoleFixPosition = CL_ConsoleFixPosition_t(0x44A430); + CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount = CL_GetLocalClientActiveCount_t(0x5BAD90); + CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum = CL_ControllerIndexFromClientNum_t(0x449E30); Cmd_AddCommand_t Cmd_AddCommand = Cmd_AddCommand_t(0x470090); Cmd_AddServerCommand_t Cmd_AddServerCommand = Cmd_AddServerCommand_t(0x4DCE00); @@ -128,6 +130,8 @@ namespace Game Dvar_RegisterVec3Color_t Dvar_RegisterVec3Color = Dvar_RegisterVec3Color_t(0x4918B0); Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = Dvar_GetUnpackedColorByName_t(0x406530); + Dvar_GetString_t Dvar_GetString = Dvar_GetString_t(0x4EC6B0); + Dvar_GetVariantString_t Dvar_GetVariantString = Dvar_GetVariantString_t(0x4C47E0); Dvar_FindVar_t Dvar_FindVar = Dvar_FindVar_t(0x4D5390); Dvar_InfoString_Big_t Dvar_InfoString_Big = Dvar_InfoString_Big_t(0x4D98A0); Dvar_SetCommand_t Dvar_SetCommand = Dvar_SetCommand_t(0x4EE430); @@ -253,6 +257,8 @@ namespace Game Live_GetPrestige_t Live_GetPrestige = Live_GetPrestige_t(0x430F90); Live_GetXp_t Live_GetXp = Live_GetXp_t(0x404C60); + LiveStorage_GetStat_t LiveStorage_GetStat = LiveStorage_GetStat_t(0x471F60); + Scr_AddSourceBuffer_t Scr_AddSourceBuffer = Scr_AddSourceBuffer_t(0x61ABC0); PC_ReadToken_t PC_ReadToken = PC_ReadToken_t(0x4ACCD0); @@ -498,8 +504,8 @@ namespace Game int* g_streamPosIndex = reinterpret_cast(0x16E5578); bool* g_lobbyCreateInProgress = reinterpret_cast(0x66C9BC2); - party_t** partyIngame = reinterpret_cast(0x1081C00); - PartyData_s** partyData = reinterpret_cast(0x107E500); + PartyData* g_lobbyData = reinterpret_cast(0x1081C00); + PartyData* g_partyData = reinterpret_cast(0x107E500); int* numIP = reinterpret_cast(0x64A1E68); netIP_t* localIP = reinterpret_cast(0x64A1E28); @@ -552,7 +558,7 @@ namespace Game scrVmPub_t* scrVmPub = reinterpret_cast(0x2040CF0); scrVarPub_t* scrVarPub = reinterpret_cast(0x201A408); - clientstate_t* clcState = reinterpret_cast(0xB2C540); + clientState_t* clcState = reinterpret_cast(0xB2C540); GfxScene* scene = reinterpret_cast(0x6944914); @@ -616,6 +622,11 @@ namespace Game clientConnection_t* clientConnections = reinterpret_cast(0xA1E878); + unsigned int* playerCardUIStringIndex = reinterpret_cast(0x62CD7A8); + char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38] = reinterpret_cast(0x62CB4F8); + + GamerSettingState* gamerSettings = reinterpret_cast(0x107D3E8); + void Sys_LockRead(FastCriticalSection* critSect) { InterlockedIncrement(&critSect->readCount); @@ -836,8 +847,8 @@ namespace Game { for (auto i = 0; i < *svs_clientCount; ++i) { - if (svs_clients[i].state != clientstate_t::CS_FREE - && svs_clients[i].netchan.remoteAddress.type == netadrtype_t::NA_BOT) + if (svs_clients[i].state != CS_FREE + && svs_clients[i].netchan.remoteAddress.type == NA_BOT) { SV_GameDropClient(i, "GAME_GET_TO_COVER"); } @@ -1746,6 +1757,5 @@ namespace Game ret } } - #pragma optimize("", on) } diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 2b0d352b..f7828d12 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -97,7 +97,7 @@ namespace Game typedef void(__cdecl * CG_SetupWeaponDef_t)(int localClientNum, unsigned int weapIndex); extern CG_SetupWeaponDef_t CG_SetupWeaponDef; - typedef char*(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, size_t size); + typedef int(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, int size); extern CL_GetClientName_t CL_GetClientName; typedef int(__cdecl * CL_IsCgameInitialized_t)(); @@ -139,6 +139,12 @@ namespace Game typedef void(__cdecl * CL_ConsoleFixPosition_t)(); extern CL_ConsoleFixPosition_t CL_ConsoleFixPosition; + typedef int(__cdecl * CL_GetLocalClientActiveCount_t)(); + extern CL_GetLocalClientActiveCount_t CL_GetLocalClientActiveCount; + + typedef int(__cdecl * CL_ControllerIndexFromClientNum_t)(int localActiveClientNum); + extern CL_ControllerIndexFromClientNum_t CL_ControllerIndexFromClientNum; + typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); extern Cmd_AddCommand_t Cmd_AddCommand; @@ -325,6 +331,12 @@ namespace Game typedef void(__cdecl * Dvar_GetUnpackedColorByName_t)(const char* dvarName, float* expandedColor); extern Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName; + typedef char*(__cdecl* Dvar_GetString_t)(const char* dvarName); + extern Dvar_GetString_t Dvar_GetString; + + typedef char*(__cdecl * Dvar_GetVariantString_t)(const char* dvarName); + extern Dvar_GetVariantString_t Dvar_GetVariantString; + typedef dvar_t*(__cdecl * Dvar_FindVar_t)(const char* dvarName); extern Dvar_FindVar_t Dvar_FindVar; @@ -456,7 +468,7 @@ namespace Game typedef void(__cdecl * Image_Release_t)(GfxImage* image); extern Image_Release_t Image_Release; - typedef char*(__cdecl * Info_ValueForKey_t)(const char* infoString, const char* key); + typedef char*(__cdecl * Info_ValueForKey_t)(const char* s, const char* key); extern Info_ValueForKey_t Info_ValueForKey; typedef void(__cdecl * Key_SetCatcher_t)(int localClientNum, int catcher); @@ -651,6 +663,9 @@ namespace Game typedef int(__cdecl * Live_GetXp_t)(int controllerIndex); extern Live_GetXp_t Live_GetXp; + typedef int(__cdecl * LiveStorage_GetStat_t)(int controllerIndex, int index); + extern LiveStorage_GetStat_t LiveStorage_GetStat; + typedef char*(__cdecl * Scr_AddSourceBuffer_t)(const char* filename, const char* extFilename, const char* codePos, bool archive); extern Scr_AddSourceBuffer_t Scr_AddSourceBuffer; @@ -663,16 +678,16 @@ namespace Game typedef void(__cdecl * PC_SourceError_t)(int, const char*, ...); extern PC_SourceError_t PC_SourceError; - typedef int(__cdecl * Party_GetMaxPlayers_t)(party_s* party); + typedef int(__cdecl * Party_GetMaxPlayers_t)(PartyData* party); extern Party_GetMaxPlayers_t Party_GetMaxPlayers; - typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData_s* party); + typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData* party); extern PartyHost_CountMembers_t PartyHost_CountMembers; typedef netadr_t *(__cdecl * PartyHost_GetMemberAddressBySlot_t)(int unk, void *party, const int slot); extern PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot; - typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData_s* party, const int clientNum); + typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData* party, const int clientNum); extern PartyHost_GetMemberName_t PartyHost_GetMemberName; typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data); @@ -1116,6 +1131,10 @@ namespace Game typedef void*(__cdecl * Z_VirtualAlloc_t)(int size); extern Z_VirtualAlloc_t Z_VirtualAlloc; + constexpr std::size_t STATIC_MAX_LOCAL_CLIENTS = 1; + constexpr std::size_t MAX_LOCAL_CLIENTS = 1; + constexpr std::size_t MAX_CLIENTS = 18; + extern XAssetHeader* DB_XAssetPool; extern unsigned int* g_poolSize; @@ -1150,8 +1169,8 @@ namespace Game extern int* g_streamPosIndex; extern bool* g_lobbyCreateInProgress; - extern party_t** partyIngame; - extern PartyData_s** partyData; + extern PartyData* g_lobbyData; + extern PartyData* g_partyData; extern int* numIP; extern netIP_t* localIP; @@ -1205,7 +1224,7 @@ namespace Game extern scrVmPub_t* scrVmPub; extern scrVarPub_t* scrVarPub; - extern clientstate_t* clcState; + extern clientState_t* clcState; extern GfxScene* scene; @@ -1273,6 +1292,12 @@ namespace Game extern clientConnection_t* clientConnections; + constexpr std::size_t PLAYER_CARD_UI_STRING_COUNT = 18; + extern unsigned int* playerCardUIStringIndex; + extern char (*playerCardUIStringBuf)[PLAYER_CARD_UI_STRING_COUNT][38]; + + extern GamerSettingState* gamerSettings; + void Sys_LockRead(FastCriticalSection* critSect); void Sys_UnlockRead(FastCriticalSection* critSect); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index aaa59f7a..0801b417 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -232,7 +232,7 @@ namespace Game CS_CONNECTED = 0x3, CS_CLIENTLOADING = 0x4, CS_ACTIVE = 0x5, - } clientstate_t; + } clientState_t; enum serverState_t { @@ -1611,6 +1611,32 @@ namespace Game unsigned int playerCardNameplate; }; + enum PlayerCardClientLookupType + { + PLAYERCARD_LOOKUP_SCRIPTSLOT = 0x0, + PLAYERCARD_LOOKUP_LIVEPROFILE_CLIENT = 0x1, + PLAYERCARD_LOOKUP_LIVEPROFILE_CONTROLLER = 0x2, + PLAYERCARD_LOOKUP_LOBBY = 0x3, + PLAYERCARD_LOOKUP_MYTEAM = 0x4, + PLAYERCARD_LOOKUP_ENEMYTEAM = 0x5, + PLAYERCARD_LOOKUP_COUNT = 0x6, + }; + + struct PlayerCardData + { + unsigned int lastUpdateTime; + unsigned int titleIndex; + unsigned int iconIndex; + unsigned int nameplateIndex; + int rank; + int prestige; + team_t team; + char name[32]; + char clanAbbrev[5]; + }; + + static_assert(sizeof(PlayerCardData) == 0x44); + enum usercmdButtonBits { CMD_BUTTON_ATTACK = 0x1, @@ -5082,18 +5108,6 @@ namespace Game char uiName[32]; }; - typedef struct party_s - { - unsigned char pad1[544]; - int privateSlots; - int publicSlots; - } party_t; - - typedef struct PartyData_s - { - DWORD unk; - } PartyData_t; - struct fileInIwd_s { unsigned int pos; @@ -6046,15 +6060,66 @@ namespace Game VISIONSETCOUNT } visionSetMode_t; + enum hintType_t + { + HINT_NONE = 0x0, + HINT_NOICON = 0x1, + HINT_ACTIVATE = 0x2, + HINT_HEALTH = 0x3, + HINT_FRIENDLY = 0x4, + FIRST_WEAPON_HINT = 0x5, + LAST_WEAPON_HINT = 0x57C, + HINT_NUM_HINTS = 0x57D, + }; + + struct playerTeamState_t + { + int location; + }; + + struct clientSession_t + { + sessionState_t sessionState; + int forceSpectatorClient; + int killCamEntity; + int killCamLookAtEntity; + int status_icon; + int archiveTime; + int score; + int deaths; + int kills; + int assists; + unsigned __int16 scriptPersId; + clientConnected_t connected; + usercmd_s cmd; + usercmd_s oldcmd; + int localClient; + int predictItemPickup; + char newnetname[16]; + int maxHealth; + int enterTime; + playerTeamState_t teamState; + int voteCount; + int teamVoteCount; + float moveSpeedScaleMultiplier; + int viewmodelIndex; + int noSpectate; + int teamInfo; + clientState_s cs; + int psOffsetTime; + int hasRadar; + int isRadarBlocked; + int radarMode; + int weaponHudIconOverrides[6]; + unsigned int unusableEntFlags[64]; + float spectateDefaultPos[3]; + float spectateDefaultAngles[3]; + }; + typedef struct gclient_s { playerState_s ps; - sessionState_t sessionState; // 12572 - unsigned char __pad0[40]; - clientConnected_t connected; // 12616 - unsigned char __pad1[144]; - team_t team; // 12764 - unsigned char __pad2[436]; + clientSession_t sess; int flags; // 13204 int spectatorClient; int lastCmdTime; @@ -6065,7 +6130,13 @@ namespace Game unsigned char __pad3[324]; // 13232 int visionDuration[5]; char visionName[5][64]; - unsigned char __pad4[36]; + int lastStand; + int lastStandTime; + int hudElemLastAssignedSoundID; + float lockedTargetOffset[3]; + unsigned __int16 attachShieldTagName; + hintType_t hintForcedType; + int hintForcedString; } gclient_t; static_assert(sizeof(gclient_t) == 13932); @@ -6224,14 +6295,14 @@ namespace Game typedef struct client_s { - clientstate_t state; // 0 - char __pad0[4]; // 4 + clientState_t state; // 0 + int sendAsActive; // 4 int deltaMessage; // 8 char __pad1[12]; // 12 netchan_t netchan; // 24 char __pad2[20]; // 1604 const char* delayDropReason; // 1624 - char connectInfoString[1024]; // 1628 + char userinfo[1024]; // 1628 char __pad3[132096]; // 2652 int reliableSequence; // 134748 int reliableAcknowledge; // 134752 @@ -6244,7 +6315,7 @@ namespace Game char lastClientCommandString[1024]; // 134816 gentity_t* gentity; // 135840 char name[16]; // 135844 - char __pad4[4]; // 135860 + int nextReliableTime; // 135860 int lastPacketTime; // 135864 int lastConnectTime; // 135868 int snapNum; // 135872 @@ -7875,6 +7946,8 @@ namespace Game unsigned int playerCardNameplate; }; + static_assert(sizeof(clientInfo_t) == 0x52C); + struct cgs_t { int viewX; @@ -8181,6 +8254,375 @@ namespace Game static_assert(sizeof(DeferredQueue) == 0x5908); + struct GamerSettingCommonConfig + { + float viewSensitivity; + float snd_volume; + float blacklevel; + float gpadButtonLStickDeflect; + float gpadButtonRStickDeflect; + float safearea_adjusted_horizontal; + float safearea_adjusted_vertical; + int playTimeSP; + int playTimeMP; + int playTimeSO; + int percentCompleteSP; + int percentCompleteMP; + int percentCompleteSO; + float gamma; + bool hasEverPlayed_MainMenu; + bool hasEverPlayed_SP; + bool hasEverPlayed_SO; + bool hasEverPlayed_MP; + bool invertPitch; + bool autoAim; + bool delicateFlower; + char gpadButtonsConfig[32]; + char gpadSticksConfig[32]; + }; + + struct KeyPairStringData + { + short index; + short maxSize; + }; + + union KeyPairDataUnion + { + char byteVal; + bool boolVal; + short shortVal; + int intVal; + float floatVal; + KeyPairStringData stringData; + }; + + struct GamerSettingKeyPair + { + char type; + char unused[3]; + KeyPairDataUnion u; + }; + + struct GamerSettingExeConfig + { + int playlist; + bool mapPrefs[16]; + char clanPrefix[5]; + }; + + struct GamerSettingState + { + bool isProfileLoggedIn; + bool errorOnRead; + GamerSettingCommonConfig commonConfig; + GamerSettingKeyPair commonKeyPairs[50]; + char commonKeyPairsStringPool[512]; + GamerSettingExeConfig exeConfig; + GamerSettingKeyPair exeKeyPairs[50]; + char exeKeyPairsStringPool[512]; + }; + + static_assert(sizeof(GamerSettingState) == 0x7C0); + + struct SessionStaticData + { + char* sessionName; + bool registerUsersWithVoice; + }; + + enum IWNetServerSessionStatus + { + SESSION_ONCLIENTONLY = 0x0, + SESSION_BEINGCREATED = 0x1, + SESSION_CREATED = 0x2, + SESSION_BEINGDELETED = 0x3, + }; + + struct IWNetServerInfoAboutPlayer + { + bool active; + __int64 uid; + char skill; + char teamIndex; + int mapPackFlags; + }; + + struct IWNetSessionStatus + { + IWNetServerSessionStatus status; + int sessionId; + int lastHeartbeatSent; + bool needsUpdate; + bool updatingPlayers; + int newPlayerCount; + IWNetServerInfoAboutPlayer pendingServerInfoForPlayers[18]; + }; + + struct XSESSION_INFO + { + XNKID sessionID; + XNADDR hostAddress; + XNKEY keyExchangeKey; + }; + + struct ClientInfo + { + bool registered; + bool voiceRegistered; + unsigned __int64 xuid; + int natType; + netadr_t addr; + int voiceConnectivityBits; + int lastConnectivityTestTime; + bool muted; + bool privateSlot; + }; + + struct NomineeInfo + { + int upload; + int NAT; + bool onLSP; + int connectivity; + int cpuSpeed; + int avgPing; + }; + + struct RegisteredUser + { + bool active; + unsigned __int64 xuid; + }; + + struct SessionDynamicData + { + bool sessionHandle; + IWNetSessionStatus iwnetServerSessionStatus; + XSESSION_INFO sessionInfo; + bool keysGenerated; + bool sessionStartCalled; + unsigned __int64 sessionNonce; + int privateSlots; + int publicSlots; + int flags; + bool qosListenEnabled; + ClientInfo users[18]; + int voiceConnectivityBits; + int sessionCreateController; + int sessionDeleteTime; + RegisteredUser internalRegisteredUsers[18]; + }; + + struct SessionData + { + SessionStaticData staticData; + SessionDynamicData dyn; + }; + + struct BestHostData + { + int nominee; + NomineeInfo info; + int lastHeardFrom; + int lastSentTo; + }; + + struct BandwidthTestPerClientData + { + int bytesReceived; + }; + + struct BandwidthTestData + { + int testIndex; + int testClientNum; + int startTimeArbitrator; + int announceTime; + int winnerClientNum; + BandwidthTestPerClientData clientData[18]; + char testClientName[32]; + bool inProgress; + int startTime; + int roundsComplete; + bool receiving; + int receiveIndex; + int receiveStartTime; + int receiveBytes; + int resultsSendTime; + }; + + struct MigrateData + { + bool migrateActive; + bool weAreArbitrating; + int arbitratorClientNum; + int indexBits; + int startTime; + int timeoutDuration; + int lastBroadcastTime; + bool decidedOurNominee; + BestHostData bestHost; + int expectedNewHost; + BandwidthTestData bandwidthTestData; + }; + + struct QoSData + { + float percent; + }; + + struct PartyInfo + { + bool active; + XSESSION_INFO info; + int occupiedPublicSlots; + int occupiedPrivateSlots; + int numPublicSlots; + int numPrivateSlots; + int pingBias; + int ping; + int upload; + int desirability; + }; + + struct PartyMember + { + char status; + bool headsetPresent; + char gamertag[32]; + char clanAbbrev[5]; + int qport; + char challenge[6]; + int lastPacketTime; + int lastHeartbeatTime; + int lastPartyStateAck; + XNADDR xnaddr; + int availableMapPackFlags; + int ackedMembers; + XNKID privatePartyId; + int subpartyIndex; + int trueSkill; + int rank; + int prestige; + int team; + unsigned __int16 score; + int deaths; + bool vetoedMap; + unsigned int playerCardIcon; + unsigned int playerCardTitle; + unsigned int playerCardNameplate; + int voiceConnectivityBits; + bool invited; + int natType; + unsigned __int64 player; + bool migrateHeardFrom; + int migratePingTime; + int migratePing; + bool migrateNominated; + NomineeInfo migrateNomineeInfo; + }; + + struct SubpartyInfo + { + int members[18]; + int count; + int skill; + int score; + int team; + }; + + struct PartyHostDetails + { + int partyListSlot; + netadr_t addr; + XSESSION_INFO sessionInfo; + int lastPacketTime; + int lastPacketSentTime; + int numPrivateSlots; + int numPublicSlots; + int hostNum; + bool accepted; + char challenge[6]; + }; + + struct PartyHostData + { + int partyStateChangeTime; + int partyStateLastSendTime; + int expectedPlayers; + int vetoPassTime; + bool vetoPossible; + bool preloadingMap; + bool firstLobby; + bool migrateAfterRound; + bool stopAfterRound; + int partyCreationTime; + }; + + struct PartyData + { + SessionData* session; + SessionData* presenceSession; + SessionData* searchSession; + MigrateData migrateData; + QoSData qosData; + PartyInfo* partyList; + int partyListSize; + PartyMember partyMembers[18]; + SubpartyInfo subparties[18]; + int subpartyCount; + PartyHostDetails currentHost; + PartyHostDetails potentialHost; + PartyHostData hostData; + unsigned __int64 lobbySteamID; + int areWeHost; + int joiningAnotherParty; + int searchingForGames; + int inParty; + int party_systemActive; + bool veto; + int vetoTime; + int headsetPresent; + int headsetTime; + int clanAbbrevTime; + int rankTime; + int playerCardTime; + int uploadSentTime; + int voiceBitsTime; + int idTime; + int availableMapPackFlagsTime; + int searchStartTime; + int searchEndTime; + int joinAttemptForUI; + int lastMergeTime; + int mergeAttemptStartTime; + int originalPartiesInList; + int partyId; + int nextSessionSearchTime; + int mapPackFlags; + int lastPartyStateTime; + int gameStartTime; + int interEndTime; + int inactiveKeepaliveTime; + int hostTimeouts; + char lobbyFlags; + PartyData* partyToNotify; + bool registeredWithArbitration; + bool rejoining; + int partyStatePacketCount; + int partyStateLastMemberIndex; + int unfinishedPartServerTimes[2]; + msg_t partyStatePartMsgs[2]; + char partyStatePartMsgBufs[2][1400]; + char lastEntries[8]; + int currentEntry; + char axisWins; + char alliesWins; + }; + + static_assert(sizeof(PartyData) == 0x23D8); + #pragma endregion #ifndef IDA From 269f4840117eb741c65bdc18a2bdd6571d3b240a Mon Sep 17 00:00:00 2001 From: Edo Date: Sun, 17 Jul 2022 00:15:15 +0200 Subject: [PATCH 48/77] [ClanTags] Fix (#361) --- src/Components/Modules/ClanTags.cpp | 26 +++++++++++++------------- src/Components/Modules/Dedicated.cpp | 1 + src/Game/Functions.cpp | 2 ++ src/Game/Functions.hpp | 3 +++ 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Components/Modules/ClanTags.cpp b/src/Components/Modules/ClanTags.cpp index 89086c11..bc0da39e 100644 --- a/src/Components/Modules/ClanTags.cpp +++ b/src/Components/Modules/ClanTags.cpp @@ -44,7 +44,7 @@ namespace Components } else { - strncpy_s(ClientState[i], clanTag, _TRUNCATE); + Game::I_strncpyz(ClientState[i], clanTag, sizeof(ClientState[0]) / sizeof(char)); } } } @@ -74,20 +74,21 @@ namespace Components std::memset(saneNameBuf, 0, sizeof(saneNameBuf)); auto* saneName = saneNameBuf; - const auto* currentName = Game::Dvar_GetString("clanName"); + const auto* currentName = ClanName->current.string; if (currentName) { auto nameLen = std::strlen(currentName); - for (std::size_t i = 0; i < nameLen; ++i) + for (std::size_t i = 0; (i < nameLen) && (i < sizeof(saneNameBuf)); ++i) { auto curChar = CL_FilterChar(static_cast(currentName[i])); if (curChar > 0) { - *saneName++ = curChar & 0xFF; + *saneName++ = (curChar & 0xFF); } } - Game::Dvar_SetStringByName("clanName", saneNameBuf); + saneNameBuf[sizeof(saneNameBuf) - 1] = '\0'; + Game::Dvar_SetString(ClanName, saneNameBuf); } } @@ -96,7 +97,7 @@ namespace Components assert(static_cast(controllerIndex) < Game::MAX_LOCAL_CLIENTS); CL_SanitizeClanName(); - strncpy_s(Game::gamerSettings[0].exeConfig.clanPrefix, Game::Dvar_GetString("clanName"), _TRUNCATE); + Game::I_strncpyz(Game::gamerSettings[0].exeConfig.clanPrefix, ClanName->current.string, sizeof(Game::GamerSettingExeConfig::clanPrefix)); return Game::gamerSettings[controllerIndex].exeConfig.clanPrefix; } @@ -121,10 +122,8 @@ namespace Components } else { - strncpy_s(ClientState[clientNum], clanAbbrev, _TRUNCATE); + Game::I_strncpyz(ClientState[clientNum], clanAbbrev, sizeof(ClientState[0]) / sizeof(char)); } - - SendClanTagsToClients(); } void __declspec(naked) ClanTags::ClientUserinfoChanged_Stub() @@ -185,7 +184,7 @@ namespace Components void ClanTags::PlayerCards_SetCachedPlayerData(Game::PlayerCardData* data, const int clientNum) { - strncpy_s(data->clanAbbrev, ClientState[clientNum], _TRUNCATE); + Game::I_strncpyz(data->clanAbbrev, ClientState[clientNum], sizeof(Game::PlayerCardData::clanAbbrev)); } void __declspec(naked) ClanTags::PlayerCards_SetCachedPlayerData_Stub() @@ -214,7 +213,7 @@ namespace Components Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForClient_Stub(const unsigned int clientIndex) { auto* result = Utils::Hook::Call(0x46C0F0)(clientIndex); - strncpy_s(result->clanAbbrev, ClientState[clientIndex], _TRUNCATE); + Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast(clientIndex)), sizeof(Game::PlayerCardData::clanAbbrev)); return result; } @@ -222,7 +221,8 @@ namespace Components Game::PlayerCardData* ClanTags::PlayerCards_GetLiveProfileDataForController_Stub(const unsigned int controllerIndex) { auto* result = Utils::Hook::Call(0x463B90)(controllerIndex); - strncpy_s(result->clanAbbrev, GamerProfile_GetClanName(static_cast(controllerIndex)), _TRUNCATE); // controllerIndex should always be 0 + // controllerIndex should always be 0 + Game::I_strncpyz(result->clanAbbrev, GamerProfile_GetClanName(static_cast(controllerIndex)), sizeof(Game::PlayerCardData::clanAbbrev)); return result; } @@ -230,7 +230,7 @@ namespace Components Game::PlayerCardData* ClanTags::PlayerCards_GetPartyMemberData(const int localClientNum, const Game::PlayerCardClientLookupType lookupType, const unsigned int memberIndex) { auto* result = Utils::Hook::Call(0x4A4A90)(localClientNum, lookupType, memberIndex); - strncpy_s(result->clanAbbrev, ClientState[memberIndex], _TRUNCATE); // controllerIndex should always be 0 + Game::I_strncpyz(result->clanAbbrev, ClientState[memberIndex], sizeof(Game::PlayerCardData::clanAbbrev)); return result; } diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 436a13a4..e2172144 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -294,6 +294,7 @@ namespace Components Scheduler::Loop([] { CardTitles::SendCustomTitlesToClients(); + ClanTags::SendClanTagsToClients(); }, Scheduler::Pipeline::SERVER, 10s); // Heartbeats diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 5e4b2893..00d96a3f 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -471,6 +471,8 @@ namespace Game Z_VirtualAlloc_t Z_VirtualAlloc = Z_VirtualAlloc_t(0x4CFBA0); + I_strncpyz_t I_strncpyz = I_strncpyz_t(0x4D6F80); + 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 f7828d12..cc0b30f8 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1131,6 +1131,9 @@ namespace Game typedef void*(__cdecl * Z_VirtualAlloc_t)(int size); extern Z_VirtualAlloc_t Z_VirtualAlloc; + typedef void(__cdecl * I_strncpyz_t)(char* dest, const char* src, int destsize); + extern I_strncpyz_t I_strncpyz; + constexpr std::size_t STATIC_MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_LOCAL_CLIENTS = 1; constexpr std::size_t MAX_CLIENTS = 18; From 617d9ad6547f8f0db88cf6d03a8dd461d80fe74f Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Mon, 18 Jul 2022 13:01:29 +0200 Subject: [PATCH 49/77] Prefix custom GSC functions with "iw4x_" (#362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prefix custom GSC functions with a "iw4x_" client prefix to preserve older mods & for better backward & forward compatibility * Prefix bot GSC functions too * Updated comments * Undo formatting * Affect functions aswell * Add deprecation warnings & auto upgrade of function names for botwarfare needs * add iw4x_prefix * Addressed review * ADdress review² * Remove choice to add clientprefix or not * Fixes & follow some of laupetin's suggestions * Fixe copy paste * use constexpr const char* * Update message Co-authored-by: Louvenarde Co-authored-by: Diavolo Co-authored-by: rackover --- src/Components/Modules/Bots.cpp | 12 +-- src/Components/Modules/Chat.cpp | 2 +- src/Components/Modules/ClientCommand.cpp | 12 +-- src/Components/Modules/GSC/IO.cpp | 6 +- src/Components/Modules/GSC/Script.cpp | 96 +++++++++++++++---- src/Components/Modules/GSC/Script.hpp | 26 ++++- .../Modules/GSC/ScriptExtension.cpp | 14 +-- src/Components/Modules/GSC/ScriptStorage.cpp | 12 +-- src/Components/Modules/UserInfo.cpp | 8 +- 9 files changed, 131 insertions(+), 57 deletions(-) diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 72c3836b..77d041d8 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -119,10 +119,10 @@ namespace Components void Bots::AddMethods() { - Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot(); - Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient(); + Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self iw4x_IsBot(); + Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self iw4x_IsTestClient(); - Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); + Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: iw4x_BotStop(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -137,7 +137,7 @@ namespace Components g_botai[entref.entnum].active = true; }); - Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: BotWeapon(); + Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: iw4x_BotWeapon(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -160,7 +160,7 @@ namespace Components g_botai[entref.entnum].active = true; }); - Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: BotAction(); + Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: iw4x_BotAction(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -201,7 +201,7 @@ namespace Components Game::Scr_ParamError(0, "^1BotAction: Unknown action.\n"); }); - Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: BotMovement(, ); + Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: iw4x_BotMovement(, ); { const auto* ent = Game::GetPlayerEntity(entref); diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index 37c55e37..b2f105e6 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -389,7 +389,7 @@ namespace Components void Chat::AddScriptFunctions() { - Script::AddFunction("OnPlayerSay", [] // gsc: OnPlayerSay() + Script::AddFunction("OnPlayerSay", [] // gsc: iw4x_OnPlayerSay() { if (Game::Scr_GetNumParam() != 1) { diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 19057580..1b39fa3f 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -351,7 +351,7 @@ namespace Components void ClientCommand::AddScriptFunctions() { - Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: Noclip(); + Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: iw4x_Noclip(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -372,7 +372,7 @@ namespace Components } }); - Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: Ufo(); + Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: iw4x_Ufo(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -393,7 +393,7 @@ namespace Components } }); - Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(); + Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: iw4x_God(); { auto* ent = Game::GetEntity(entref); @@ -414,7 +414,7 @@ namespace Components } }); - Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(); + Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: iw4x_Demigod(); { auto* ent = Game::GetEntity(entref); @@ -435,7 +435,7 @@ namespace Components } }); - Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(); + Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: iw4x_Notarget(); { auto* ent = Game::GetEntity(entref); @@ -456,7 +456,7 @@ namespace Components } }); - Script::AddFunction("DropAllBots", [] // gsc: DropAllBots(); + Script::AddFunction("DropAllBots", [] // gsc: iw4x_DropAllBots(); { Game::SV_DropAllBots(); }); diff --git a/src/Components/Modules/GSC/IO.cpp b/src/Components/Modules/GSC/IO.cpp index 159944eb..2eaa1203 100644 --- a/src/Components/Modules/GSC/IO.cpp +++ b/src/Components/Modules/GSC/IO.cpp @@ -8,7 +8,7 @@ namespace Components void IO::AddScriptFunctions() { - Script::AddFunction("FileWrite", [] // gsc: FileWrite(, , ) + Script::AddFunction("FileWrite", [] // gsc: iw4x_FileWrite(, , ) { const auto* path = Game::Scr_GetString(0); auto* text = Game::Scr_GetString(1); @@ -53,7 +53,7 @@ namespace Components } }); - Script::AddFunction("FileRead", [] // gsc: FileRead() + Script::AddFunction("FileRead", [] // gsc: iw4x_FileRead() { const auto* path = Game::Scr_GetString(0); @@ -83,7 +83,7 @@ namespace Components Game::Scr_AddString(FileSystem::FileReader(scriptData).getBuffer().data()); }); - Script::AddFunction("FileExists", [] // gsc: FileExists() + Script::AddFunction("FileExists", [] // gsc: iw4x_FileExists() { const auto* path = Game::Scr_GetString(0); diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index 6c2e0be3..9f8680c8 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -3,9 +3,37 @@ namespace Components { + std::unordered_map Script::CustomScrFunctions; + std::unordered_map Script::CustomScrMethods; + + // This was added on the 17th of July 2022 to help transition current mods to + // the new prefixed functions. Look at the clock! If it's more than three months + // later than this date... remove this! + std::unordered_set Script::DeprecatedFunctionsAndMethods = + { + "isbot", + "istestclient", + "botstop", + "botweapon", + "botmovement", + "botaction", + "onplayersay", + "fileread", + "filewrite", + "fileexists", + "getsystemmilliseconds", + "exec", + "printconsole", + "arecontrolsfrozen", + "setping", + "setname", + "getname", + "dropallbots", + "httpget", + "httpcancel" + }; + std::string Script::ScriptName; - std::unordered_map Script::CustomScrFunctions; - std::unordered_map Script::CustomScrMethods; std::vector Script::ScriptNameStack; unsigned short Script::FunctionName; std::unordered_map Script::ScriptBaseProgramNum; @@ -273,32 +301,50 @@ namespace Components Game::GScr_LoadGameTypeScript(); } - void Script::AddFunction(const char* name, Game::BuiltinFunction func, int type) + void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type) { - Game::BuiltinFunctionDef toAdd; - toAdd.actionString = name; + const auto functionName = Script::ClientPrefix + name; + + Script::ScriptFunction toAdd; toAdd.actionFunc = func; toAdd.type = type; - CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd); + CustomScrFunctions.insert_or_assign(Utils::String::ToLower(functionName), toAdd); } - void Script::AddMethod(const char* name, Game::BuiltinMethod func, int type) + void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type) { - Game::BuiltinMethodDef toAdd; - toAdd.actionString = name; + const auto functionName = Script::ClientPrefix + name; + + Script::ScriptMethod toAdd; toAdd.actionFunc = func; toAdd.type = type; - CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd); + CustomScrMethods.insert_or_assign(Utils::String::ToLower(functionName), toAdd); + } + + bool Script::IsDeprecated(const std::string& name) + { + return Script::DeprecatedFunctionsAndMethods.contains(name); } Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type) { if (pName != nullptr) { - const auto got = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName)); + auto name = Utils::String::ToLower(*pName); + if (IsDeprecated(name)) + { + Toast::Show("cardicon_gumby", "WARNING!", std::format("{} uses the deprecated function {}", Script::ScriptName, name), 2048); + Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "*** DEPRECATION WARNING ***\n"); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated builtin {} from {}! This method or function should be prefixed with '{}'. Please update your mod!\n", name, Script::ScriptName, Script::ClientPrefix); + Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "***************************\n"); + + name = Script::ClientPrefix + name; // Fixes it automatically + } + + const auto got = Script::CustomScrFunctions.find(name); // If no function was found let's call game's function if (got != Script::CustomScrFunctions.end()) { @@ -317,12 +363,24 @@ namespace Components return Utils::Hook::Call(0x5FA2B0)(pName, type); // BuiltIn_GetFunction } - Game::BuiltinMethod Script::BuiltIn_GetMethod(const char** pName, int* type) + Game::BuiltinMethod Script::BuiltIn_GetMethodStub(const char** pName, int* type) { if (pName != nullptr) { - const auto got = Script::CustomScrMethods.find(Utils::String::ToLower(*pName)); + auto name = Utils::String::ToLower(*pName); + if (IsDeprecated(name)) + { + Toast::Show("cardicon_gumby", "WARNING!", std::format("{} uses the deprecated method {}", Script::ScriptName, name), 2048); + Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "*** DEPRECATION WARNING ***\n"); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated builtin {} from {}! This function or method should be prefixed with '{}'. Please update your mod!\n", name, Script::ScriptName, Script::ClientPrefix); + Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "***************************\n"); + + name = Script::ClientPrefix + name; // Fixes it automatically + + } + + const auto got = Script::CustomScrMethods.find(name); // If no method was found let's call game's function if (got != Script::CustomScrMethods.end()) { @@ -538,7 +596,7 @@ namespace Components void Script::AddFunctions() { - Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) + Script::AddFunction("ReplaceFunc", [] // gsc: iw4x_ReplaceFunc(, ) { if (Game::Scr_GetNumParam() != 2) { @@ -553,7 +611,7 @@ namespace Components }); // System time - Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() + Script::AddFunction("GetSystemMilliseconds", [] // gsc: iw4x_GetSystemMilliseconds() { SYSTEMTIME time; GetSystemTime(&time); @@ -562,7 +620,7 @@ namespace Components }); // Executes command to the console - Script::AddFunction("Exec", [] // gsc: Exec() + Script::AddFunction("Exec", [] // gsc: iw4x_Exec() { const auto str = Game::Scr_GetString(0); @@ -576,7 +634,7 @@ namespace Components }); // Allow printing to the console even when developer is 0 - Script::AddFunction("PrintConsole", [] // gsc: PrintConsole() + Script::AddFunction("PrintConsole", [] // gsc: iw4x_PrintConsole() { for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i) { @@ -593,7 +651,7 @@ namespace Components }); // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 - Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); + Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self iw4x_AreControlsFrozen(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -626,7 +684,7 @@ namespace Components // Fetch custom functions Utils::Hook(0x44E72E, Script::BuiltIn_GetFunctionStub, HOOK_CALL).install()->quick(); // Scr_GetFunction - Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethod, HOOK_CALL).install()->quick(); // Scr_GetMethod + Utils::Hook(0x4EC8DD, Script::BuiltIn_GetMethodStub, HOOK_CALL).install()->quick(); // Scr_GetMethod Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/GSC/Script.hpp b/src/Components/Modules/GSC/Script.hpp index 1adfe31e..f92d9f3a 100644 --- a/src/Components/Modules/GSC/Script.hpp +++ b/src/Components/Modules/GSC/Script.hpp @@ -7,17 +7,32 @@ namespace Components public: Script(); - static void AddFunction(const char* name, Game::BuiltinFunction func, int type = 0); - static void AddMethod(const char* name, Game::BuiltinMethod func, int type = 0); + static void AddFunction(const std::string& name, Game::BuiltinFunction func, bool type = false); + static void AddMethod(const std::string& name, Game::BuiltinMethod func, bool type = false); static Game::client_t* GetClient(const Game::gentity_t* gentity); static const char* GetCodePosForParam(int index); private: + struct ScriptFunction + { + Game::BuiltinFunction actionFunc; + bool type; + }; + + struct ScriptMethod + { + Game::BuiltinMethod actionFunc; + bool type; + }; + + static constexpr auto* ClientPrefix = "iw4x_"; + static std::unordered_map CustomScrFunctions; + static std::unordered_map CustomScrMethods; + static std::unordered_set DeprecatedFunctionsAndMethods; + static std::string ScriptName; - static std::unordered_map CustomScrFunctions; - static std::unordered_map CustomScrMethods; static std::vector ScriptNameStack; static unsigned short FunctionName; static std::unordered_map ScriptBaseProgramNum; @@ -46,8 +61,9 @@ namespace Components static void Scr_StartupGameType_Stub(); static void GScr_LoadGameTypeScript_Stub(); + static bool IsDeprecated(const std::string& name); static Game::BuiltinFunction BuiltIn_GetFunctionStub(const char** pName, int* type); - static Game::BuiltinMethod BuiltIn_GetMethod(const char** pName, int* type); + static Game::BuiltinMethod BuiltIn_GetMethodStub(const char** pName, int* type); static void StoreScriptBaseProgramNumStub(); static void StoreScriptBaseProgramNum(); diff --git a/src/Components/Modules/GSC/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp index 9dda1b9f..f6551e27 100644 --- a/src/Components/Modules/GSC/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -116,7 +116,7 @@ namespace Components void ScriptExtension::AddFunctions() { // Misc functions - Script::AddFunction("ToUpper", [] // gsc: ToUpper() + Script::AddFunction("ToUpper", [] // gsc: iw4x_ToUpper() { const auto scriptValue = Game::Scr_GetConstString(0); const auto* string = Game::SL_ConvertToString(scriptValue); @@ -161,7 +161,7 @@ namespace Components }); // Func present on IW5 - Script::AddFunction("StrICmp", [] // gsc: StrICmp(, ) + Script::AddFunction("StrICmp", [] // gsc: iw4x_StrICmp(, ) { const auto value1 = Game::Scr_GetConstString(0); const auto value2 = Game::Scr_GetConstString(1); @@ -173,7 +173,7 @@ namespace Components }); // Func present on IW5 - Script::AddFunction("IsEndStr", [] // gsc: IsEndStr(, ) + Script::AddFunction("IsEndStr", [] // gsc: iw4x_IsEndStr(, ) { const auto* s1 = Game::Scr_GetString(0); const auto* s2 = Game::Scr_GetString(1); @@ -187,7 +187,7 @@ namespace Components Game::Scr_AddBool(Utils::String::EndsWith(s1, s2)); }); - Script::AddFunction("IsArray", [] // gsc: IsArray() + Script::AddFunction("IsArray", [] // gsc: iw4x_IsArray() { const auto type = Game::Scr_GetType(0); @@ -211,7 +211,7 @@ namespace Components void ScriptExtension::AddMethods() { // ScriptExtension methods - Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp() + Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self iw4x_GetIp() { const auto* ent = Game::GetPlayerEntity(entref); const auto* client = Script::GetClient(ent); @@ -224,7 +224,7 @@ namespace Components Game::Scr_AddString(ip.data()); }); - Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing() + Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self iw4x_GetPing() { const auto* ent = Game::GetPlayerEntity(entref); const auto* client = Script::GetClient(ent); @@ -232,7 +232,7 @@ namespace Components Game::Scr_AddInt(client->ping); }); - Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() + Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self iw4x_SetPing() { auto ping = Game::Scr_GetInt(0); diff --git a/src/Components/Modules/GSC/ScriptStorage.cpp b/src/Components/Modules/GSC/ScriptStorage.cpp index b169d1ea..8a1c47c1 100644 --- a/src/Components/Modules/GSC/ScriptStorage.cpp +++ b/src/Components/Modules/GSC/ScriptStorage.cpp @@ -8,7 +8,7 @@ namespace Components void ScriptStorage::AddScriptFunctions() { - Script::AddFunction("StorageSet", [] // gsc: StorageSet(, ); + Script::AddFunction("StorageSet", [] // gsc: iw4x_StorageSet(, ); { const auto* key = Game::Scr_GetString(0); const auto* value = Game::Scr_GetString(1); @@ -22,7 +22,7 @@ namespace Components Data.insert_or_assign(key, value); }); - Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(); + Script::AddFunction("StorageRemove", [] // gsc: iw4x_StorageRemove(); { const auto* key = Game::Scr_GetString(0); @@ -41,7 +41,7 @@ namespace Components Data.erase(key); }); - Script::AddFunction("StorageGet", [] // gsc: StorageGet(); + Script::AddFunction("StorageGet", [] // gsc: iw4x_StorageGet(); { const auto* key = Game::Scr_GetString(0); @@ -61,7 +61,7 @@ namespace Components Game::Scr_AddString(data.data()); }); - Script::AddFunction("StorageHas", [] // gsc: StorageHas(); + Script::AddFunction("StorageHas", [] // gsc: iw4x_StorageHas(); { const auto* key = Game::Scr_GetString(0); @@ -74,7 +74,7 @@ namespace Components Game::Scr_AddBool(Data.contains(key)); }); - Script::AddFunction("StorageDump", [] // gsc: StorageDump(); + Script::AddFunction("StorageDump", [] // gsc: iw4x_StorageDump(); { if (Data.empty()) { @@ -87,7 +87,7 @@ namespace Components FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump()); }); - Script::AddFunction("StorageClear", [] // gsc: StorageClear(); + Script::AddFunction("StorageClear", [] // gsc: iw4x_StorageClear(); { Data.clear(); }); diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index 87a15245..c7fbbd04 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -44,7 +44,7 @@ namespace Components void UserInfo::AddScriptMethods() { - Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self SetName() + Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self iw4x_SetName() { const auto* ent = Game::GetPlayerEntity(entref); const auto* name = Game::Scr_GetString(0); @@ -59,7 +59,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self ResetName() + Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self iw4x_ResetName() { const auto* ent = Game::GetPlayerEntity(entref); @@ -68,7 +68,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self setClanTag() + Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self iw4x_setClanTag() { const auto* ent = Game::GetPlayerEntity(entref); const auto* clanName = Game::Scr_GetString(0); @@ -83,7 +83,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self ResetClanTag() + Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self iw4x_ResetClanTag() { const auto* ent = Game::GetPlayerEntity(entref); From 75cf9b6b85bee201acb3c12bac937461ecc665d6 Mon Sep 17 00:00:00 2001 From: Dss0 <7338300+Dss0@users.noreply.github.com> Date: Mon, 18 Jul 2022 19:09:39 +0200 Subject: [PATCH 50/77] Remove obsolete DLC Store (#363) --- src/Components/Modules/Menus.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index 89b9c89f..32221eeb 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -855,7 +855,6 @@ namespace Components Menus::Add("ui_mp/popup_friends.menu"); Menus::Add("ui_mp/menu_first_launch.menu"); Menus::Add("ui_mp/startup_messages.menu"); - Menus::Add("ui_mp/pc_store.menu"); Menus::Add("ui_mp/iw4x_credits.menu"); Menus::Add("ui_mp/resetclass.menu"); Menus::Add("ui_mp/popup_customtitle.menu"); From f18d5aff5d65bb259ff3416aef888750a083a5d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Jul 2022 17:30:20 +0000 Subject: [PATCH 51/77] Bump deps/libtomcrypt from `06a81ae` to `8fd5dad` Bumps [deps/libtomcrypt](https://github.com/libtom/libtomcrypt) from `06a81ae` to `8fd5dad`. - [Release notes](https://github.com/libtom/libtomcrypt/releases) - [Commits](https://github.com/libtom/libtomcrypt/compare/06a81aeb227424182125363f7554fad5146d6d2a...8fd5dad96b56beb53b5cf199cb63fb76dfba32bb) --- updated-dependencies: - dependency-name: deps/libtomcrypt dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/libtomcrypt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/libtomcrypt b/deps/libtomcrypt index 06a81aeb..8fd5dad9 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit 06a81aeb227424182125363f7554fad5146d6d2a +Subproject commit 8fd5dad96b56beb53b5cf199cb63fb76dfba32bb From 63ce4095e06e8fff9581ca6a503f9ce1029d347e Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Mon, 18 Jul 2022 23:29:34 +0200 Subject: [PATCH 52/77] Safer clans (#366) * Safer clans * Remove pointer check Co-authored-by: Louvenarde --- src/Components/Modules/ClanTags.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Components/Modules/ClanTags.cpp b/src/Components/Modules/ClanTags.cpp index bc0da39e..c2acad4c 100644 --- a/src/Components/Modules/ClanTags.cpp +++ b/src/Components/Modules/ClanTags.cpp @@ -74,6 +74,8 @@ namespace Components std::memset(saneNameBuf, 0, sizeof(saneNameBuf)); auto* saneName = saneNameBuf; + + assert(ClanName); const auto* currentName = ClanName->current.string; if (currentName) { @@ -95,9 +97,10 @@ namespace Components char* ClanTags::GamerProfile_GetClanName(int controllerIndex) { assert(static_cast(controllerIndex) < Game::MAX_LOCAL_CLIENTS); + assert(ClanName); CL_SanitizeClanName(); - Game::I_strncpyz(Game::gamerSettings[0].exeConfig.clanPrefix, ClanName->current.string, sizeof(Game::GamerSettingExeConfig::clanPrefix)); + Game::I_strncpyz(Game::gamerSettings[controllerIndex].exeConfig.clanPrefix, ClanName->current.string, sizeof(Game::GamerSettingExeConfig::clanPrefix)); return Game::gamerSettings[controllerIndex].exeConfig.clanPrefix; } @@ -237,11 +240,11 @@ namespace Components ClanTags::ClanTags() { - Scheduler::Once([] + Events::OnClientInit([] { ClanName = Game::Dvar_RegisterString("clanName", "", Game::DVAR_ARCHIVE, "Your clan abbreviation"); - }, Scheduler::Pipeline::MAIN); + }); std::memset(&ClientState, 0, sizeof(char[Game::MAX_CLIENTS][5])); From e556b835cdd045a71a383023edb3f53beed16885 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Tue, 19 Jul 2022 14:02:53 +0200 Subject: [PATCH 53/77] [Weapons] Fix *gone right* (gone thankful for JTAG) --- .gitignore | 4 ++-- src/Components/Modules/QuickPatch.cpp | 1 - src/Components/Modules/Weapon.cpp | 10 ++-------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 5278875b..3b801e92 100644 --- a/.gitignore +++ b/.gitignore @@ -149,5 +149,5 @@ ida/* # User scripts user*.bat -# Premake binary -#premake5.exe +### Visual Studio Code +.vscode/ diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index 575a7b9d..f4e31ad4 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -404,7 +404,6 @@ namespace Components // fs_game fixes Utils::Hook::Nop(0x4A5D74, 2); // remove fs_game profiles Utils::Hook::Set(0x4081FD, 0xEB); // defaultweapon - Utils::Hook::Set(0x452C1D, 0xEB); // LoadObj weaponDefs // filesystem init default_mp.cfg check Utils::Hook::Nop(0x461A9E, 5); diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index c528fab0..ae82ada9 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -558,15 +558,9 @@ namespace Components Utils::Hook::Nop(0x4B3670, 5); Utils::Hook(0x57B4F0, LoadNoneWeaponHookStub, HOOK_JUMP).install()->quick(); - // Don't load bounce sounds for now, it causes crashes - // TODO: Actually check the weaponfiles and/or reset the soundtable correctly! - //Utils::Hook::Nop(0x57A360, 5); - //Utils::Hook::Nop(0x57A366, 6); - Utils::Hook::Nop(0x5795E9, 2); - // Clear weapons independently from fs_game - //Utils::Hook::Nop(0x452C1D, 2); - //Utils::Hook::Nop(0x452C24, 5); + Utils::Hook::Nop(0x452C1D, 2); + Utils::Hook::Nop(0x452C24, 5); Utils::Hook(0x59E341, CG_UpdatePrimaryForAltModeWeapon_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x48BB25, CG_SelectWeaponIndex_Stub, HOOK_JUMP).install()->quick(); From ad362c74b36c472cf2d52967becef9470f3fca03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Jul 2022 17:15:46 +0000 Subject: [PATCH 54/77] Bump deps/GSL from `d9fc52e` to `330583f` Bumps [deps/GSL](https://github.com/microsoft/GSL) from `d9fc52e` to `330583f`. - [Release notes](https://github.com/microsoft/GSL/releases) - [Commits](https://github.com/microsoft/GSL/compare/d9fc52e20e32fcc804502480a781120b210afd41...330583f47800c60cf001239550d291d16274756a) --- updated-dependencies: - dependency-name: deps/GSL dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- deps/GSL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/GSL b/deps/GSL index d9fc52e2..330583f4 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit d9fc52e20e32fcc804502480a781120b210afd41 +Subproject commit 330583f47800c60cf001239550d291d16274756a From 54cdc425eea8f1b5ceb9ee791bf35f4c04659a4d Mon Sep 17 00:00:00 2001 From: Diavolo Date: Wed, 20 Jul 2022 21:47:54 +0200 Subject: [PATCH 55/77] Speed up loading by not requiring extra zones Co-authored-by: diamante0018 Co-authored-by: Louvenarde --- src/Components/Modules/Maps.cpp | 113 +++++++++++--------------------- src/Components/Modules/Maps.hpp | 12 ++-- 2 files changed, 46 insertions(+), 79 deletions(-) diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index bb093f0e..6a00b673 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -130,38 +130,30 @@ namespace Components Maps::SPMap = false; Maps::CurrentMainZone = zoneInfo->name; - Maps::CurrentDependencies.clear(); - for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i) - { - if (std::regex_match(zoneInfo->name, std::regex(i->first))) - { - if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), i->second) == Maps::CurrentDependencies.end()) - { - Maps::CurrentDependencies.push_back(i->second); - } - } - } - - Utils::Memory::Allocator allocator; - auto teams = Maps::GetTeamsForMap(Maps::CurrentMainZone); - - auto dependencies = Maps::GetDependenciesForMap(Maps::CurrentMainZone); - Utils::Merge(&Maps::CurrentDependencies, dependencies.data(), dependencies.size()); + auto dependencies = GetDependenciesForMap(zoneInfo->name); + std::vector data; Utils::Merge(&data, zoneInfo, zoneCount); + Utils::Memory::Allocator allocator; - Game::XZoneInfo team; - team.allocFlags = zoneInfo->allocFlags; - team.freeFlags = zoneInfo->freeFlags; + if (dependencies.requiresTeamZones) + { + auto teams = dependencies.requiredTeams; - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data())); - data.push_back(team); + Game::XZoneInfo team; + team.allocFlags = zoneInfo->allocFlags; + team.freeFlags = zoneInfo->freeFlags; - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data())); - data.push_back(team); + team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data())); + data.push_back(team); + team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data())); + data.push_back(team); + } + + Utils::Merge(&Maps::CurrentDependencies, dependencies.requiredMaps.data(), dependencies.requiredMaps.size()); for (unsigned int i = 0; i < Maps::CurrentDependencies.size(); ++i) { Game::XZoneInfo info; @@ -336,66 +328,42 @@ namespace Components Maps::SPMap = true; } - void Maps::AddDependency(const std::string& expression, const std::string& zone) - { - // Test expression before adding it - try - { - std::regex _(expression); - } - catch (const std::regex_error ex) - { - MessageBoxA(nullptr, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION); - return; - } - - Maps::DependencyList.push_back({expression, zone}); - } - int Maps::IgnoreEntityStub(const char* entity) { return (Utils::String::StartsWith(entity, "dyn_") || Utils::String::StartsWith(entity, "node_") || Utils::String::StartsWith(entity, "actor_")); } - std::vector Maps::GetDependenciesForMap(const std::string& map) + Maps::MapDependencies Maps::GetDependenciesForMap(const std::string& map) { - for (int i = 0; i < *Game::arenaCount; ++i) - { - Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; - if (arena->mapName == map) - { - for (int j = 0; j < ARRAYSIZE(arena->keys); ++j) - { - if (arena->keys[j] == "dependency"s) - { - return Utils::String::Split(arena->values[j], ' '); - } - } - } - } + std::string teamAxis = "opforce_composite"; + std::string teamAllies = "us_army"; - return {}; - } - - std::pair Maps::GetTeamsForMap(const std::string& map) - { - std::string team_axis = "opforce_composite"; - std::string team_allies = "us_army"; + Maps::MapDependencies dependencies{}; for (int i = 0; i < *Game::arenaCount; ++i) { Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; if (arena->mapName == map) { - for (int j = 0; j < ARRAYSIZE(arena->keys); ++j) + for (std::size_t j = 0; j < std::extent_v; ++j) { - if (arena->keys[j] == "allieschar"s) + const auto* key = arena->keys[j]; + const auto* value = arena->values[j]; + if (key == "dependency"s) { - team_allies = arena->values[j]; + dependencies.requiredMaps = Utils::String::Split(arena->values[j], ' '); } - else if (arena->keys[j] == "axischar"s) + else if (key == "allieschar"s) { - team_axis = arena->values[j]; + teamAllies = value; + } + else if (key == "axischar"s) + { + teamAxis = value; + } + else if (key == "useteamzones"s) + { + dependencies.requiresTeamZones = Utils::String::ToLower(value) == "true"s; } } @@ -403,7 +371,9 @@ namespace Components } } - return {team_axis, team_allies}; + dependencies.requiredTeams = std::make_pair(teamAllies, teamAxis); + + return dependencies; } void Maps::PrepareUsermap(const char* mapname) @@ -905,13 +875,6 @@ namespace Components // Load usermap arena file Utils::Hook(0x630A88, Maps::LoadArenaFileStub, HOOK_CALL).install()->quick(); - // Dependencies - //Maps::AddDependency("oilrig", "mp_subbase"); - //Maps::AddDependency("gulag", "mp_subbase"); - //Maps::AddDependency("invasion", "mp_rust"); - //Maps::AddDependency("co_hunted", "mp_storm"); - //Maps::AddDependency("mp_shipment", "mp_shipment_long"); - // Allow hiding specific smodels Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index 80e015c2..5a9a26c8 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -50,10 +50,6 @@ namespace Components ~Maps(); static void HandleAsSPMap(); - static void AddDependency(const std::string& expression, const std::string& zone); - - static std::pair GetTeamsForMap(const std::string& map); - static std::vector GetDependenciesForMap(const std::string& map); static std::string CurrentMainZone; static const char* UserMapFiles[4]; @@ -76,6 +72,13 @@ namespace Components std::vector maps; }; + struct MapDependencies + { + std::vector requiredMaps; + std::pair requiredTeams; + bool requiresTeamZones; + }; + static bool SPMap; static UserMapContainer UserMap; static std::vector DlcPacks; @@ -91,6 +94,7 @@ namespace Components static void UnloadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); static void OverrideMapEnts(Game::MapEnts* ents); + static MapDependencies GetDependenciesForMap(const std::string& map); static int IgnoreEntityStub(const char* entity); From c601a1b97e6a1aa2d1086f675b771957bfd84749 Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Thu, 21 Jul 2022 00:22:49 +0200 Subject: [PATCH 56/77] Fix fs xmodsurfs (#372) * This commit cured my crashes! * Use correct thread for Ents swapping * Mutex * Cleanup * Cleanup2 Co-authored-by: Diavolo Co-authored-by: Louvenarde --- src/Components/Modules/FileSystem.cpp | 35 ++++++++++++++++++--------- src/Components/Modules/FileSystem.hpp | 7 +++--- src/Components/Modules/Maps.cpp | 2 +- src/Components/Modules/Zones.cpp | 13 ++++------ src/Game/Structs.hpp | 11 +++++++++ 5 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/Components/Modules/FileSystem.cpp b/src/Components/Modules/FileSystem.cpp index dddf4c42..0f3f3894 100644 --- a/src/Components/Modules/FileSystem.cpp +++ b/src/Components/Modules/FileSystem.cpp @@ -6,17 +6,28 @@ namespace Components std::recursive_mutex FileSystem::FSMutex; Utils::Memory::Allocator FileSystem::MemAllocator; - void FileSystem::File::read() + void FileSystem::File::read(Game::FsThread thread) { - char* _buffer = nullptr; - int size = Game::FS_ReadFile(this->filePath.data(), &_buffer); + std::lock_guard _(FileSystem::FSMutex); - this->buffer.clear(); + assert(!filePath.empty()); - if (size >= 0) + int handle; + const auto len = Game::FS_FOpenFileReadForThread(filePath.data(), &handle, thread); + + if (handle) { - this->buffer.append(_buffer, size); - Game::FS_FreeFile(_buffer); + auto* buf = AllocateFile(len + 1); + + Game::FS_Read(buf, len, handle); + + buf[len] = '\0'; + + Game::FS_FCloseFile(handle); + + this->buffer = buf; + + FreeFile(buf); } } @@ -185,7 +196,7 @@ namespace Components else *buffer = nullptr; if (!path) return -1; - std::lock_guard _(FileSystem::Mutex); + std::lock_guard _(FileSystem::Mutex); FileSystem::FileReader reader(path); int size = reader.getSize(); @@ -256,13 +267,13 @@ namespace Components void FileSystem::FsStartupSync(const char* a1) { - std::lock_guard _(FileSystem::FSMutex); + std::lock_guard _(FileSystem::FSMutex); return Utils::Hook::Call(0x4823A0)(a1); // FS_Startup } void FileSystem::FsRestartSync(int a1, int a2) { - std::lock_guard _(FileSystem::FSMutex); + std::lock_guard _(FileSystem::FSMutex); Maps::GetUserMap()->freeIwd(); Utils::Hook::Call(0x461A50)(a1, a2); // FS_Restart Maps::GetUserMap()->reloadIwd(); @@ -270,7 +281,7 @@ namespace Components void FileSystem::FsShutdownSync(int a1) { - std::lock_guard _(FileSystem::FSMutex); + std::lock_guard _(FileSystem::FSMutex); Maps::GetUserMap()->freeIwd(); Utils::Hook::Call(0x4A46C0)(a1); // FS_Shutdown } @@ -283,7 +294,7 @@ namespace Components int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) { - std::lock_guard _(FileSystem::FSMutex); + std::lock_guard _(FileSystem::FSMutex); return Game::Load_Texture(loadDef, image); } diff --git a/src/Components/Modules/FileSystem.hpp b/src/Components/Modules/FileSystem.hpp index ddcc4b59..e6c17e44 100644 --- a/src/Components/Modules/FileSystem.hpp +++ b/src/Components/Modules/FileSystem.hpp @@ -18,8 +18,9 @@ namespace Components class File : public AbstractFile { public: - File() {}; - File(const std::string& file) : filePath(file) { this->read(); }; + File() = default; + File(std::string file) : filePath{std::move(file)} { this->read(); }; + File(std::string file, Game::FsThread thread) : filePath{std::move(file)} { this->read(thread); }; bool exists() override { return !this->buffer.empty(); }; std::string getName() override { return this->filePath; }; @@ -29,7 +30,7 @@ namespace Components std::string filePath; std::string buffer; - void read(); + void read(Game::FsThread thread = Game::FS_THREAD_MAIN); }; class RawFile : public AbstractFile diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index bb093f0e..c379f86a 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -255,7 +255,7 @@ namespace Components } static std::string mapEntities; - FileSystem::File ents(name + ".ents"); + FileSystem::File ents(name + ".ents", Game::FS_THREAD_DATABASE); if (ents.exists()) { mapEntities = ents.getBuffer(); diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index bf3de6f7..1add1496 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -184,18 +184,15 @@ namespace Components __asm { pushad + push edi call Zones::LoadXModelLodInfo add esp, 4h + popad - mov eax, [esp + 8h] - push eax - add eax, 8 - push eax - call Game::Load_XModelSurfsFixup - add esp, 8h - + push 0x4EA703 // Return address + push 0x40D7A0 // Load_XModelSurfsFixup retn } } @@ -3628,7 +3625,7 @@ namespace Components Utils::Hook(0x45AE3D, Zones::LoadRandomFxGarbage, HOOK_CALL).install()->quick(); Utils::Hook(0x495938, Zones::LoadFxElemDefArrayStub, HOOK_CALL).install()->quick(); Utils::Hook(0x45ADA0, Zones::LoadFxElemDefStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4EA6FE, Zones::LoadXModelLodInfoStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4EA6FE, Zones::LoadXModelLodInfoStub, HOOK_JUMP).install()->quick(); Utils::Hook(0x410D90, Zones::LoadXModel, HOOK_CALL).install()->quick(); Utils::Hook(0x4925C8, Zones::LoadXSurfaceArray, HOOK_CALL).install()->quick(); Utils::Hook(0x4F4D0D, Zones::LoadGameWorldSp, HOOK_CALL).install()->quick(); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 0801b417..566baddd 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -96,6 +96,17 @@ namespace Game ASSET_TYPE_INVALID = -1, }; + enum FsThread + { + FS_THREAD_MAIN = 0x0, + FS_THREAD_STREAM = 0x1, + FS_THREAD_DATABASE = 0x2, + FS_THREAD_BACKEND = 0x3, + FS_THREAD_SERVER = 0x4, + FS_THREAD_COUNT = 0x5, + FS_THREAD_INVALID = 0x6, + }; + enum materialSurfType_t { SURF_TYPE_DEFAULT, From 63a7a011c261b6b6b5423327caf5a214d565a18b Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 11:16:16 +0200 Subject: [PATCH 57/77] [General] Add pull request template --- .github/pull_request_template.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..211f261d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +**What does this PR do?** + +Thanks for the contribution! Please provide a concise description of the problem this request solves. + +**How does this PR change IW4x's behavior?** + +Are there any breaking changes? Will any existing behavior change? +Specify the new expected behaviour if this PR adds a new gameplay feature or alters an existing game mechanic. You may provide an image or video showing the changes. + +**Anything else we should know?** + +Add any other context about your changes here. + +**Did you check all the boxes?** + +- [ ] Focus on a single fix or feature; remove any unrelated formatting or code changes +- [ ] Mention any [related issues](https://github.com/XLabsProject/iw4x-client/issues) (put `closes #XXXX` in comment to auto-close issue when PR is merged) +- [ ] Follow our [coding conventions](https://github.com/XLabsProject/iw4x-client/blob/master/CODESTYLE.md) +- [ ] Minimize the number of commits From 378f0eb4746d005cb93fcf817df44925b72bc1dc Mon Sep 17 00:00:00 2001 From: Edo Date: Thu, 21 Jul 2022 12:38:24 +0200 Subject: [PATCH 58/77] Update pull_request_template.md --- .github/pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 211f261d..512b32c9 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ Thanks for the contribution! Please provide a concise description of the problem **How does this PR change IW4x's behavior?** -Are there any breaking changes? Will any existing behavior change? +Are there any breaking changes? Will any existing behaviour change? Specify the new expected behaviour if this PR adds a new gameplay feature or alters an existing game mechanic. You may provide an image or video showing the changes. **Anything else we should know?** From 7ee57de3e3d440e4d27b9e43e49fe48460cfe95a Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 14:04:14 +0200 Subject: [PATCH 59/77] [ClanTags] Remove hooked function that must not be called when in-game --- src/Components/Modules/ClanTags.cpp | 11 +---------- src/Components/Modules/ClanTags.hpp | 1 - 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/src/Components/Modules/ClanTags.cpp b/src/Components/Modules/ClanTags.cpp index c2acad4c..c3247f2b 100644 --- a/src/Components/Modules/ClanTags.cpp +++ b/src/Components/Modules/ClanTags.cpp @@ -230,14 +230,6 @@ namespace Components return result; } - Game::PlayerCardData* ClanTags::PlayerCards_GetPartyMemberData(const int localClientNum, const Game::PlayerCardClientLookupType lookupType, const unsigned int memberIndex) - { - auto* result = Utils::Hook::Call(0x4A4A90)(localClientNum, lookupType, memberIndex); - Game::I_strncpyz(result->clanAbbrev, ClientState[memberIndex], sizeof(Game::PlayerCardData::clanAbbrev)); - - return result; - } - ClanTags::ClanTags() { Events::OnClientInit([] @@ -261,7 +253,7 @@ namespace Components return false; }); - + Utils::Hook(0x430B00, Dvar_InfoString_Stub, HOOK_CALL).install()->quick(); Utils::Hook(0x44532F, ClientUserinfoChanged_Stub, HOOK_JUMP).install()->quick(); @@ -276,7 +268,6 @@ namespace Components Utils::Hook(0x458DF4, PlayerCards_SetCachedPlayerData_Stub, HOOK_JUMP).install()->quick(); Utils::Hook(0x62EAB6, PlayerCards_GetLiveProfileDataForClient_Stub, HOOK_CALL).install()->quick(); Utils::Hook(0x62EAC3, PlayerCards_GetLiveProfileDataForController_Stub, HOOK_CALL).install()->quick(); - Utils::Hook(0x62EAE8, PlayerCards_GetPartyMemberData, HOOK_CALL).install()->quick(); // clanName in CG_Obituary Utils::Hook(0x586DD6, PlayerName::GetClientName, HOOK_CALL).install()->quick(); diff --git a/src/Components/Modules/ClanTags.hpp b/src/Components/Modules/ClanTags.hpp index dc1c7952..1893d620 100644 --- a/src/Components/Modules/ClanTags.hpp +++ b/src/Components/Modules/ClanTags.hpp @@ -43,6 +43,5 @@ namespace Components static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForClient_Stub(unsigned int clientIndex); static Game::PlayerCardData* PlayerCards_GetLiveProfileDataForController_Stub(unsigned int controllerIndex); - static Game::PlayerCardData* PlayerCards_GetPartyMemberData(int localClientNum, Game::PlayerCardClientLookupType lookupType, unsigned int memberIndex); }; } From e88811e98024656936af07e3c3a29aa5cf1e8c09 Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Thu, 21 Jul 2022 14:33:12 +0200 Subject: [PATCH 60/77] i swear i'm gonna kill him (#378) Co-authored-by: rackover Co-authored-by: Edo --- src/Components/Modules/FileSystem.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Components/Modules/FileSystem.cpp b/src/Components/Modules/FileSystem.cpp index 0f3f3894..39816e09 100644 --- a/src/Components/Modules/FileSystem.cpp +++ b/src/Components/Modules/FileSystem.cpp @@ -19,14 +19,15 @@ namespace Components { auto* buf = AllocateFile(len + 1); - Game::FS_Read(buf, len, handle); + [[maybe_unused]] auto bytesRead = Game::FS_Read(buf, len, handle); + + assert(bytesRead == len); buf[len] = '\0'; Game::FS_FCloseFile(handle); - this->buffer = buf; - + this->buffer.append(buf, len); FreeFile(buf); } } From 0a30d60f42c7dcecc93e201068b97d0fcb30b78c Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 18:32:08 +0200 Subject: [PATCH 61/77] fix typo --- src/Components/Modules/GSC/Script.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index 9f8680c8..638d03f4 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -27,7 +27,7 @@ namespace Components "arecontrolsfrozen", "setping", "setname", - "getname", + "resetname", "dropallbots", "httpget", "httpcancel" From da61ab42ec3040e9f6dbb456b47a09702e091d7a Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 18:56:16 +0200 Subject: [PATCH 62/77] Fix some justified warnings & more --- src/Components/Modules/ClientCommand.cpp | 10 +++++----- src/Components/Modules/GSC/Script.cpp | 2 +- src/Components/Modules/GSC/ScriptExtension.cpp | 8 ++++---- src/Components/Modules/Theatre.cpp | 2 +- src/Components/Modules/UserInfo.cpp | 2 ++ 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 1b39fa3f..cc552799 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -355,7 +355,7 @@ namespace Components { const auto* ent = Game::GetPlayerEntity(entref); - if (Game::Scr_GetNumParam() >= 1u) + if (Game::Scr_GetNumParam() >= 1) { if (Game::Scr_GetInt(0)) { @@ -376,7 +376,7 @@ namespace Components { const auto* ent = Game::GetPlayerEntity(entref); - if (Game::Scr_GetNumParam() >= 1u) + if (Game::Scr_GetNumParam() >= 1) { if (Game::Scr_GetInt(0)) { @@ -397,7 +397,7 @@ namespace Components { auto* ent = Game::GetEntity(entref); - if (Game::Scr_GetNumParam() >= 1u) + if (Game::Scr_GetNumParam() >= 1) { if (Game::Scr_GetInt(0)) { @@ -418,7 +418,7 @@ namespace Components { auto* ent = Game::GetEntity(entref); - if (Game::Scr_GetNumParam() >= 1u) + if (Game::Scr_GetNumParam() >= 1) { if (Game::Scr_GetInt(0)) { @@ -439,7 +439,7 @@ namespace Components { auto* ent = Game::GetEntity(entref); - if (Game::Scr_GetNumParam() >= 1u) + if (Game::Scr_GetNumParam() >= 1) { if (Game::Scr_GetInt(0)) { diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index 9f8680c8..85ae8602 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -495,7 +495,7 @@ namespace Components return ""; } - const auto value = &Game::scrVmPub->top[-index]; + const auto* value = &Game::scrVmPub->top[-index]; if (value->type != Game::scrParamType_t::VAR_FUNCTION) { diff --git a/src/Components/Modules/GSC/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp index f6551e27..411282dc 100644 --- a/src/Components/Modules/GSC/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -189,14 +189,14 @@ namespace Components Script::AddFunction("IsArray", [] // gsc: iw4x_IsArray() { - const auto type = Game::Scr_GetType(0); + auto type = Game::Scr_GetType(0); bool result; if (type == Game::VAR_POINTER) { - const auto ptr_type = Game::Scr_GetPointerType(0); - assert(ptr_type >= Game::FIRST_OBJECT); - result = (ptr_type == Game::VAR_ARRAY); + type = Game::Scr_GetPointerType(0); + assert(type >= Game::FIRST_OBJECT); + result = (type == Game::VAR_ARRAY); } else { diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index 46285135..56e7ba05 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -73,7 +73,7 @@ namespace Components if (i + size >= sizeof(cmpData)) { - Logger::Print("Error: Writing compressed demo baseline exceeded buffer\n"); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "Writing compressed demo baseline exceeded buffer\n"); break; } diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index c7fbbd04..39b6cd63 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -52,6 +52,7 @@ namespace Components if (name == nullptr) { Game::Scr_ParamError(0, "^1SetName: Illegal parameter!\n"); + return; } Logger::Debug("Setting name of {} to {}", ent->s.number, name); @@ -76,6 +77,7 @@ namespace Components if (clanName == nullptr) { Game::Scr_ParamError(0, "^1SetClanTag: Illegal parameter!\n"); + return; } Logger::Debug("Setting clanName of {} to {}", ent->s.number, clanName); From 61463cc9e7d1cfdcb457c99dddfbac7e40b5fd5a Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 21:30:44 +0200 Subject: [PATCH 63/77] [News] Update urls --- src/Components/Modules/News.cpp | 3 +-- src/Utils/Cache.cpp | 8 +------- src/Utils/Utils.cpp | 2 +- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Components/Modules/News.cpp b/src/Components/Modules/News.cpp index 24d0f29d..47bbab52 100644 --- a/src/Components/Modules/News.cpp +++ b/src/Components/Modules/News.cpp @@ -59,8 +59,7 @@ namespace Components UIScript::Add("visitWiki", [](UIScript::Token) { - //Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/")); - Utils::OpenUrl("https://github.com/Emosewaj/IW4x/wiki"); + Utils::OpenUrl(Utils::Cache::GetStaticUrl("/iw4x_faq")); }); UIScript::Add("visitDiscord", [](UIScript::Token) diff --git a/src/Utils/Cache.cpp b/src/Utils/Cache.cpp index cd761d76..f2e7a6f4 100644 --- a/src/Utils/Cache.cpp +++ b/src/Utils/Cache.cpp @@ -6,14 +6,8 @@ namespace Utils { "https://xlabs.dev", "https://raw.githubusercontent.com/XLabsProject/iw4x-client" - - //Links to old onion site - deprecated - //"https://iw4xcachep26muba.tor2web.xyz", - //"https://iw4xcachep26muba.onion.ws", - //"https://iw4xcachep26muba.onion.sh", - //"https://iw4xcachep26muba.onion.pet", - }; + std::string Cache::ValidUrl; std::mutex Cache::CacheMutex; diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index a214c963..6d9cd4df 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -123,7 +123,7 @@ namespace Utils void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd) { - [=]() + [=] { __try { From 48c35efedca2dcea5f0ef9e51ff468a085d10878 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Thu, 21 Jul 2022 21:41:32 +0200 Subject: [PATCH 64/77] [News] Remove unused urls --- src/Components/Modules/News.cpp | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Components/Modules/News.cpp b/src/Components/Modules/News.cpp index 47bbab52..8a75adf8 100644 --- a/src/Components/Modules/News.cpp +++ b/src/Components/Modules/News.cpp @@ -57,16 +57,6 @@ namespace Components Utils::OpenUrl(Utils::Cache::GetStaticUrl("")); }); - UIScript::Add("visitWiki", [](UIScript::Token) - { - Utils::OpenUrl(Utils::Cache::GetStaticUrl("/iw4x_faq")); - }); - - UIScript::Add("visitDiscord", [](UIScript::Token) - { - Utils::OpenUrl("https://discord.gg/sKeVmR3"); - }); - Localization::Set("MPUI_CHANGELOG_TEXT", "Loading..."); Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFAULT); From 3af56bb27fe8a2d8814021a428d53bf776406c59 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 22 Jul 2022 11:27:38 +0200 Subject: [PATCH 65/77] [Logger] Improve Hook code --- src/Components/Modules/Logger.cpp | 28 ++++++++++++---------------- src/Components/Modules/Logger.hpp | 8 ++++---- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 0c5ee6cf..a700c693 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -12,7 +12,7 @@ namespace Components return (IsWindow(Console::GetWindow()) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console"))); } - void Logger::PrintStub(const int channel, const char* message, ...) + void Logger::Print_Stub(const int channel, const char* message, ...) { char buf[4096] = {0}; @@ -164,22 +164,18 @@ namespace Components } } - void Logger::G_LogPrintfStub(const char* fmt, ...) + void Logger::G_LogPrintf_Hk(const char* fmt, ...) { - char string[1024]; - char string2[1024]; + char string[1024]{}; + char string2[1024]{}; va_list ap; va_start(ap, fmt); vsnprintf_s(string2, _TRUNCATE, fmt, ap); va_end(ap); - const auto min = Game::level->time / 1000 / 60; - const auto tens = Game::level->time / 1000 % 60 / 10; - const auto sec = Game::level->time / 1000 % 60 % 10; - - const auto len = _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", - min, tens, sec, string2); + const auto time = Game::level->time / 1000; + const auto len = _snprintf_s(string, _TRUNCATE, "%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, string2); if (Game::level->logFile != nullptr) { @@ -190,7 +186,7 @@ namespace Components Logger::NetworkLog(string, true); } - __declspec(naked) void Logger::PrintMessageStub() + __declspec(naked) void Logger::PrintMessage_Stub() { __asm { @@ -241,7 +237,7 @@ namespace Components } } - __declspec(naked) void Logger::BuildOSPathStub() + __declspec(naked) void Logger::BuildOSPath_Stub() { __asm { @@ -374,18 +370,18 @@ namespace Components Logger::Logger() { Dvar::Register("iw4x_onelog", false, Game::DVAR_LATCH | Game::DVAR_ARCHIVE, "Only write the game log to the 'userraw' OS folder"); - Utils::Hook(0x642139, Logger::BuildOSPathStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x642139, Logger::BuildOSPath_Stub, HOOK_JUMP).install()->quick(); Logger::PipeOutput(nullptr); Scheduler::Loop(Logger::Frame, Scheduler::Pipeline::SERVER); - Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintfStub, HOOK_JUMP).install()->quick(); - Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::G_LogPrintf, Logger::G_LogPrintf_Hk, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessage_Stub, HOOK_JUMP).install()->quick(); if (Loader::IsPerformingUnitTests()) { - Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::Com_Printf, Logger::Print_Stub, HOOK_JUMP).install()->quick(); } Events::OnSVInit(Logger::AddServerCommands); diff --git a/src/Components/Modules/Logger.hpp b/src/Components/Modules/Logger.hpp index cd281624..165a53a1 100644 --- a/src/Components/Modules/Logger.hpp +++ b/src/Components/Modules/Logger.hpp @@ -10,7 +10,7 @@ namespace Components static bool IsConsoleReady(); - static void PrintStub(int channel, const char* message, ...); + static void Print_Stub(int channel, const char* message, ...); static void PipeOutput(void(*callback)(const std::string&)); @@ -100,12 +100,12 @@ namespace Components static void MessagePrint(int channel, const std::string& msg); static void Frame(); - static void G_LogPrintfStub(const char* fmt, ...); - static void PrintMessageStub(); + static void G_LogPrintf_Hk(const char* fmt, ...); + static void PrintMessage_Stub(); static void PrintMessagePipe(const char* data); static void EnqueueMessage(const std::string& message); - static void BuildOSPathStub(); + static void BuildOSPath_Stub(); static void RedirectOSPath(const char* file, char* folder); static void NetworkLog(const char* data, bool gLog); From 8dd6086848907da825b32aaaae629defae19c1f3 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Fri, 22 Jul 2022 12:17:13 +0200 Subject: [PATCH 66/77] [General] Add issue templates --- .github/ISSUE_TEMPLATE/get-help.md | 30 +++++++++++++++++++++ .github/ISSUE_TEMPLATE/request-a-feature.md | 26 ++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/get-help.md create mode 100644 .github/ISSUE_TEMPLATE/request-a-feature.md diff --git a/.github/ISSUE_TEMPLATE/get-help.md b/.github/ISSUE_TEMPLATE/get-help.md new file mode 100644 index 00000000..2455e886 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/get-help.md @@ -0,0 +1,30 @@ +--- +name: Get help +about: Get help using IW4x +title: '' +labels: discussion +assignees: '' + +--- + +_Do not open an issue here if you need help with modding or have a problem getting the client to run. +It is very likely your problem will be resolved by reading the [FAQ](https://xlabs.dev/iw4x_faq) carefully. +Ask in `iw4x-support` or `iw4x-modding` channels on the [Discord](https://discord.gg/sKeVmR3) server if you still have problems. +If this does not apply, please continue by filling in the template below._ + +**What are you trying to do?** +A short, concise description of the outcome you are trying to achieve. + +**What problem are you having?** +A clear and concise description of the problem that is blocking you from your desired outcome, ex. "IW4x is crashing with this error message: ..." +Please walk us through the steps you have taken when you encountered this problem. +If IW4x is crashing, include the minidump file and the crash address in text form. Screenshots showing the message box with the crash address are not acceptable. + +**What have you tried so far?** +Describe any steps you've already taken to try to get past this issue. Have you found a workaround? + +**What version of Premake are you using?** +`iw4x.exe -version` will show you the version. Please make sure you are up to date with the latest master branch. + +**Anything else we should know?** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/request-a-feature.md b/.github/ISSUE_TEMPLATE/request-a-feature.md new file mode 100644 index 00000000..37d72635 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/request-a-feature.md @@ -0,0 +1,26 @@ +--- +name: Request a feature +about: Suggest a new feature or enhancement +title: '' +labels: feature +assignees: '' + +--- + +_Before opening a new feature request, please see [Issues](https://github.com/XLabsProject/iw4x-client/issues) and check that a similar issue does not already exist. +If this a new request, help us help you by filling in the template below._ + +**What problem will this solve?** +A clear and concise description of the problem this new feature is meant to solve, ex. "There is something wrong with the game" or "There is something wrong with IW4x's source code". +Please limit your request to a single feature; create multiple feature requests instead. + +**What might be a solution?** +A clear and concise description of what you want to happen. If you are proposing changes to IW4x's source code, tell us which component you want to be changed. +If you propose changes to the game, you may use images or videos to illustrate what aspect of the game should be changed. + +**What other alternatives have you already considered?** +A clear and concise description of any alternative solutions or features you've considered. +It may help others to find workarounds until the problem is resolved. + +**Anything else we should know?** +Add any other context or screenshots about the feature request here. From 3a3fca64e1e383d1fdd448a2587a9a20742e351f Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 23 Jul 2022 15:49:56 +0200 Subject: [PATCH 67/77] [Console] Add very useful debugging function --- src/Components/Modules/Console.cpp | 42 +++++++- src/Components/Modules/Console.hpp | 3 + src/Game/Functions.cpp | 13 ++- src/Game/Functions.hpp | 9 +- src/Game/Structs.hpp | 154 +++++++++++++++++++++++++++++ 5 files changed, 214 insertions(+), 7 deletions(-) diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index d7d18992..586d0c42 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -324,10 +324,10 @@ namespace Components va_list va; va_start(va, fmt); - _vsnprintf_s(buf, _TRUNCATE, fmt, va); + vsnprintf_s(buf, _TRUNCATE, fmt, va); va_end(va); - Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}", buf); + Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}\n", buf); Console::RefreshOutput(); @@ -534,10 +534,42 @@ namespace Components return reinterpret_cast(0x471500)(dvarName, r, g, b, a, min, max, flags, description); } + void Console::Con_ToggleConsole() + { + Game::Field_Clear(Game::g_consoleField); + if (Game::conDrawInputGlob->matchIndex >= 0 && Game::conDrawInputGlob->autoCompleteChoice[0] != '\0') + { + Game::conDrawInputGlob->matchIndex = -1; + Game::conDrawInputGlob->autoCompleteChoice[0] = '\0'; + } + + Game::g_consoleField->fixedSize = 1; + Game::con->outputVisible = false; + Game::g_consoleField->widthInPixels = *Game::g_console_field_width; + Game::g_consoleField->charHeight = *Game::g_console_char_height; + + for (std::size_t localClientNum = 0; localClientNum < Game::MAX_LOCAL_CLIENTS; ++localClientNum) + { + assert((Game::clientUIActives[0].keyCatchers & Game::KEYCATCH_CONSOLE) == (Game::clientUIActives[localClientNum].keyCatchers & Game::KEYCATCH_CONSOLE)); + Game::clientUIActives[localClientNum].keyCatchers ^= 1; + } + } + + void Console::AddConsoleCommand() + { + Command::Add("con_echo", [] + { + Console::Con_ToggleConsole(); + Game::I_strncpyz(Game::g_consoleField->buffer, "\\echo ", sizeof(Game::field_t::buffer)); + Game::g_consoleField->cursor = static_cast(std::strlen(Game::g_consoleField->buffer)); + Game::Field_AdjustScroll(Game::ScrPlace_GetFullPlacement(), Game::g_consoleField); + }); + } + Console::Console() { // Console '%s: %s> ' string - Utils::Hook::Set(0x5A44B4, "IW4x: " VERSION "> "); + Utils::Hook::Set(0x5A44B4, "IW4x MP: " VERSION "> "); // Patch console color static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f }; @@ -577,6 +609,10 @@ namespace Components // Don't resize the console Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick(); +#ifdef _DEBUG + Console::AddConsoleCommand(); +#endif + if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) { Scheduler::Loop(Console::RefreshStatus, Scheduler::Pipeline::MAIN); diff --git a/src/Components/Modules/Console.hpp b/src/Components/Modules/Console.hpp index baf414b0..baee794c 100644 --- a/src/Components/Modules/Console.hpp +++ b/src/Components/Modules/Console.hpp @@ -66,6 +66,9 @@ namespace Components static void ToggleConsole(); static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType); + static void Con_ToggleConsole(); + static void AddConsoleCommand(); + static Game::dvar_t* RegisterConColor(const char* dvarName, float r, float g, float b, float a, float min, float max, unsigned __int16 flags, const char* description); }; } diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 00d96a3f..f2e71858 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -564,13 +564,19 @@ namespace Game GfxScene* scene = reinterpret_cast(0x6944914); + Console* con = reinterpret_cast(0x9FCCF8); ConDrawInputGlob* conDrawInputGlob = reinterpret_cast(0x9FD6F8); + + int* g_console_field_width = reinterpret_cast(0x79854C); + float* g_console_char_height = reinterpret_cast(0x798550); field_t* g_consoleField = reinterpret_cast(0xA1B6B0); clientStatic_t* cls = reinterpret_cast(0xA7FE90); + clientUIActive_t* clientUIActives = reinterpret_cast(0xB2BB8A); sharedUiInfo_t* sharedUiInfo = reinterpret_cast(0x62E4B78); ScreenPlacement* scrPlaceFull = reinterpret_cast(0x10843F0); + ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); ScreenPlacement* scrPlaceView = reinterpret_cast(0x1084378); clientActive_t* clients = reinterpret_cast(0xB2C698); @@ -593,8 +599,6 @@ namespace Game FastCriticalSection* db_hashCritSect = reinterpret_cast(0x16B8A54); - ScreenPlacement* scrPlaceFullUnsafe = reinterpret_cast(0x1084460); - float (*CorrectSolidDeltas)[26][3] = reinterpret_cast(0x739BB8); // Count 26 level_locals_t* level = reinterpret_cast(0x1A831A8); @@ -1164,6 +1168,11 @@ namespace Game return GraphGetValueFromFraction(graph->knotCount, graph->knots, fraction) * graph->scale; } + ScreenPlacement* ScrPlace_GetFullPlacement() + { + return scrPlaceFull; + } + ScreenPlacement* ScrPlace_GetUnsafeFullPlacement() { return scrPlaceFullUnsafe; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index cc0b30f8..35986f50 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1231,13 +1231,19 @@ namespace Game extern GfxScene* scene; + extern Console* con; extern ConDrawInputGlob* conDrawInputGlob; + + extern int* g_console_field_width; + extern float* g_console_char_height; extern field_t* g_consoleField; extern clientStatic_t* cls; + extern clientUIActive_t* clientUIActives; extern sharedUiInfo_t* sharedUiInfo; extern ScreenPlacement* scrPlaceFull; + extern ScreenPlacement* scrPlaceFullUnsafe; extern ScreenPlacement* scrPlaceView; extern clientActive_t* clients; @@ -1266,8 +1272,6 @@ namespace Game extern FastCriticalSection* db_hashCritSect; - extern ScreenPlacement* scrPlaceFullUnsafe; - extern level_locals_t* level; extern float (*penetrationDepthTable)[PENETRATE_TYPE_COUNT][SURF_TYPE_COUNT]; @@ -1306,6 +1310,7 @@ namespace Game XModel* G_GetModel(int index); + ScreenPlacement* ScrPlace_GetFullPlacement(); ScreenPlacement* ScrPlace_GetUnsafeFullPlacement(); void UI_FilterStringForButtonAnimation(char* str, unsigned int strMaxSize); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 566baddd..5b29df09 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -8634,6 +8634,160 @@ namespace Game static_assert(sizeof(PartyData) == 0x23D8); + struct MessageLine + { + int messageIndex; + int textBufPos; + int textBufSize; + int typingStartTime; + int lastTypingSoundTime; + int flags; + }; + + struct Message + { + int startTime; + int endTime; + }; + + struct MessageWindow + { + MessageLine* lines; + Message* messages; + char* circularTextBuffer; + int textBufSize; + int lineCount; + int padding; + int scrollTime; + int fadeIn; + int fadeOut; + int textBufPos; + int firstLineIndex; + int activeLineCount; + int messageIndex; + }; + + struct MessageBuffer + { + char gamemsgText[4][2048]; + MessageWindow gamemsgWindows[4]; + MessageLine gamemsgLines[4][12]; + Message gamemsgMessages[4][12]; + char miniconText[4096]; + MessageWindow miniconWindow; + MessageLine miniconLines[100]; + Message miniconMessages[100]; + char errorText[1024]; + MessageWindow errorWindow; + MessageLine errorLines[5]; + Message errorMessages[5]; + }; + + struct Console + { + MessageWindow consoleWindow; + MessageLine consoleLines[1024]; + Message consoleMessages[1024]; + char consoleText[65536]; + char textTempLine[512]; + unsigned int lineOffset; + int displayLineOffset; + int prevChannel; + bool outputVisible; + int fontHeight; + int visibleLineCount; + int visiblePixelWidth; + float screenMin[2]; + float screenMax[2]; + MessageBuffer messageBuffer[1]; + float color[4]; + }; + + enum clientMigState_t + { + CMSTATE_INACTIVE = 0x0, + CMSTATE_OLDHOSTLEAVING = 0x1, + CMSTATE_LIMBO = 0x2, + CMSTATE_NEWHOSTCONNECT = 0x3, + CMSTATE_COUNT = 0x4, + }; + + enum MigrationVerboseState + { + MVSTATE_INACTIVE = 0x0, + MVSTATE_WAITING = 0x1, + MVSTATE_RATING = 0x2, + MVSTATE_SENDING = 0x3, + MVSTATE_MIGRATING = 0x4, + MVSTATE_COUNT = 0x5, + }; + + enum connstate_t + { + CA_DISCONNECTED = 0x0, + CA_CINEMATIC = 0x1, + CA_LOGO = 0x2, + CA_CONNECTING = 0x3, + CA_CHALLENGING = 0x4, + CA_CONNECTED = 0x5, + CA_SENDINGSTATS = 0x6, + CA_LOADING = 0x7, + CA_PRIMED = 0x8, + CA_ACTIVE = 0x9, + }; + + struct MigrationPers + { + int time; + bool stanceHeld; + StanceState stance; + StanceState stancePosition; + int stanceTime; + int cgameUserCmdWeapon; + int cgameUserCmdOffHandIndex; + unsigned int weaponSelect; + int weaponSelectTime; + int weaponForcedSelectTime; + unsigned int weaponLatestPrimaryIdx; + unsigned __int16 primaryWeaponForAlt[1400]; + int holdBreathTime; + int holdBreathInTime; + int holdBreathDelay; + float holdBreathFrac; + }; + + struct clientUIActive_t + { + bool active; + bool isRunning; + bool cgameInitialized; + bool cgameInitCalled; + bool mapPreloaded; + clientMigState_t migrationState; + MigrationPers migrationPers; + MigrationVerboseState verboseMigrationState; + int verboseMigrationData; + int keyCatchers; + bool displayHUDWithKeycatchUI; + connstate_t connectionState; + bool invited; + char itemsUnlocked[256]; + bool itemsUnlockedInited; + bool itemsUnlockedLastGameDirty; + unsigned __int16 itemsUnlockedLastGame[16]; + int itemsUnlockedLastGameCount; + char* itemsUnlockedBuffer; + int itemsUnlockedLocalClientNum; + int itemsUnlockedControllerIndex; + int itemsUnlockedStatsSource; + }; + + enum msgLocErrType_t + { + LOCMSG_SAFE = 0x0, + LOCMSG_NOERR = 0x1, + }; + #pragma endregion #ifndef IDA From 034abfe235790b4fffd3199b3ca22d35d20fb511 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 23 Jul 2022 22:16:26 +0200 Subject: [PATCH 68/77] [VisionFile] Move zone patch here --- src/Components/Modules/QuickPatch.cpp | 15 ++- src/Components/Modules/QuickPatch.hpp | 2 +- src/Components/Modules/VisionFile.cpp | 136 ++++++++++++++++---------- src/Components/Modules/VisionFile.hpp | 8 +- src/Components/Modules/Zones.cpp | 44 +-------- src/Components/Modules/Zones.hpp | 6 +- src/Game/Structs.hpp | 1 - src/Utils/String.cpp | 22 +++-- src/Utils/String.hpp | 15 +-- 9 files changed, 124 insertions(+), 125 deletions(-) diff --git a/src/Components/Modules/QuickPatch.cpp b/src/Components/Modules/QuickPatch.cpp index f4e31ad4..14e4e909 100644 --- a/src/Components/Modules/QuickPatch.cpp +++ b/src/Components/Modules/QuickPatch.cpp @@ -175,20 +175,20 @@ namespace Components } } - BOOL QuickPatch::IsDynClassnameStub(char* a1) + BOOL QuickPatch::IsDynClassnameStub(const char* classname) { - auto version = Zones::GetEntitiesZoneVersion(); + const auto version = Zones::Version(); if (version >= VERSION_LATEST_CODO) { for (auto i = 0; i < Game::spawnVars->numSpawnVars; i++) { char** kvPair = Game::spawnVars->spawnVars[i]; - auto key = kvPair[0]; - auto val = kvPair[1]; + const auto* key = kvPair[0]; + const auto* val = kvPair[1]; - bool isSpecOps = strncmp(key, "script_specialops", 17) == 0; - bool isSpecOpsOnly = val[0] == '1' && val[1] == '\0'; + auto isSpecOps = std::strncmp(key, "script_specialops", 17) == 0; + auto isSpecOpsOnly = (val[0] == '1') && (val[1] == '\0'); if (isSpecOps && isSpecOpsOnly) { @@ -199,8 +199,7 @@ namespace Components } } - // Passthrough to the game's own IsDynClassname - return Utils::Hook::Call(0x444810)(a1); + return Utils::Hook::Call(0x444810)(classname); // IsDynClassname } void QuickPatch::CL_KeyEvent_OnEscape() diff --git a/src/Components/Modules/QuickPatch.hpp b/src/Components/Modules/QuickPatch.hpp index b5cc5d9f..52fa8c46 100644 --- a/src/Components/Modules/QuickPatch.hpp +++ b/src/Components/Modules/QuickPatch.hpp @@ -23,7 +23,7 @@ namespace Components static void ClientEventsFireWeaponStub(); static void ClientEventsFireWeaponMeleeStub(); - static BOOL IsDynClassnameStub(char* a1); + static BOOL IsDynClassnameStub(const char* classname); static void CL_KeyEvent_OnEscape(); static void CL_KeyEvent_ConsoleEscape_Stub(); diff --git a/src/Components/Modules/VisionFile.cpp b/src/Components/Modules/VisionFile.cpp index 463a9d01..1a1eb6da 100644 --- a/src/Components/Modules/VisionFile.cpp +++ b/src/Components/Modules/VisionFile.cpp @@ -2,76 +2,104 @@ namespace Components { - const char* VisionFile::DvarExceptions[] = + std::vector VisionFile::DvarExceptions = { "r_pretess", }; - void VisionFile::ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName) + std::unordered_map VisionFile::VisionReplacements { - for (std::size_t i = 0; i < ARRAYSIZE(DvarExceptions); ++i) - { - if (dvarName == DvarExceptions[i]) - { - const auto* dvar = Game::Dvar_FindVar(dvarName.data()); - const auto* parsedValue = Game::Com_ParseOnLine(&buffer); + {"511", "r_glow"}, + {"516", "r_glowRadius0"}, + {"512", "r_glowBloomCutoff"}, + {"513", "r_glowBloomDesaturation"}, + {"514", "r_glowBloomIntensity0"}, + {"520", "r_filmEnable"}, + {"522", "r_filmContrast"}, + {"521", "r_filmBrightness"}, + {"523", "r_filmDesaturation"}, + {"524", "r_filmDesaturationDark"}, + {"525", "r_filmInvert"}, + {"526", "r_filmLightTint"}, + {"527", "r_filmMediumTint"}, + {"528", "r_filmDarkTint"}, + {"529", "r_primaryLightUseTweaks"}, + {"530", "r_primaryLightTweakDiffuseStrength"}, + {"531", "r_primaryLightTweakSpecularStrength"}, + }; - assert(dvar != nullptr); + bool VisionFile::ApplyExemptDvar(const char* dvarName, const char** buffer, const char* filename) + { + for (auto& exceptions : DvarExceptions) + { + if (!_stricmp(dvarName, exceptions.data())) + { + const auto* dvar = Game::Dvar_FindVar(dvarName); + const auto* parsedValue = Game::Com_ParseOnLine(buffer); + + assert(dvar); + assert(parsedValue); Game::Dvar_SetFromStringFromSource(dvar, parsedValue, Game::DvarSetSource::DVAR_SOURCE_INTERNAL); - Logger::Print("Overriding '{}' from '{}'\n", dvarName, fileName); + Logger::Print("Overriding '{}' from '{}'\n", dvar->name, filename); // Successfully found and tried to apply the string value to the dvar - return; + return true; } } - Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM, - "WARNING: unknown dvar \'%s\' in file \'%s\'\n", dvarName.data(), fileName.data()); + return false; } - // Gets the dvar name and value and attemps to apply it to the vision settings - void VisionFile::ApplyValueToSettings(const std::string& key, const char* buffer, - const std::string& fileName, Game::visionSetVars_t* settings) + bool VisionFile::LoadVisionSettingsFromBuffer(const char* buffer, const char* filename, Game::visionSetVars_t* settings) { - for (std::size_t i = 0; i < 21; ++i) + assert(settings); + + bool wasRead[21]{}; + Game::Com_BeginParseSession(filename); + + while (true) { - // Must be case insensitive comparison - if (key == Utils::String::ToLower(Game::visionDefFields[i].name)) + auto* token = Game::Com_Parse(&buffer); + + if (!*token) { - auto* const dvarValue = Game::Com_ParseOnLine(&buffer); - - if (!Game::ApplyTokenToField(i, dvarValue, settings)) - { - Game::Com_PrintWarning(Game::conChannel_t::CON_CHANNEL_SYSTEM, - "WARNING: malformed dvar \'%s\' in file \'%s\'\n", dvarValue, fileName.data()); - - // Failed to apply the value. Check that sscanf can actually parse the value - return; - } - - // Successfully found and applied the value to the settings - return; + break; } - } - // Dvar not found in visionDefFields, let's try to see if it's a 'patched' dvar - ApplyExemptDvar(key, buffer, fileName); - } + auto found = false; + auto fieldNum = 0; - bool VisionFile::LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings) - { - assert(settings != nullptr); - assert(fileName != nullptr); + const auto it = VisionReplacements.find(token); + for (fieldNum = 0; fieldNum < 21; ++fieldNum) + { + if (!wasRead[fieldNum] && !_stricmp((it == VisionReplacements.end()) ? token : it->second.data(), Game::visionDefFields[fieldNum].name)) + { + found = true; + break; + } + } - Game::Com_BeginParseSession(fileName); + if (!found) + { + if (!ApplyExemptDvar(token, &buffer, filename)) + { + Logger::Warning(Game::CON_CHANNEL_SYSTEM, "WARNING: unknown dvar '{}' in file '{}'\n", token, filename); + Game::Com_SkipRestOfLine(&buffer); + } + continue; + } - // Will split the buffer into tokens using the following delimiters: space, newlines (more?) - for (auto i = Game::Com_Parse(&buffer); *i != '\0'; i = Game::Com_Parse(&buffer)) - { - // Converting 'key' to lower case as it will be needed later - ApplyValueToSettings(Utils::String::ToLower(i), buffer, fileName, settings); - Game::Com_SkipRestOfLine(&buffer); + token = Game::Com_ParseOnLine(&buffer); + if (ApplyTokenToField(fieldNum, token, settings)) + { + wasRead[fieldNum] = true; + } + else + { + Logger::Warning(Game::CON_CHANNEL_SYSTEM, "WARNING: malformed dvar '{}' in file '{}'\n", token, filename); + Game::Com_SkipRestOfLine(&buffer); + } } Game::Com_EndParseSession(); @@ -80,15 +108,21 @@ namespace Components __declspec(naked) bool VisionFile::LoadVisionSettingsFromBuffer_Stub() { - // No need for push/pop ad guards, I have checked :) __asm { - push [esp + 0x8] // settings + push eax + pushad + + push [esp + 0x24 + 0x8] // settings push ebx // filename - push [esp + 0xC] // buffer - call VisionFile::LoadVisionSettingsFromBuffer + push [esp + 0x24 + 0xC] // buffer + call LoadVisionSettingsFromBuffer add esp, 0xC + mov [esp + 0x20], eax + popad + pop eax + ret } } diff --git a/src/Components/Modules/VisionFile.hpp b/src/Components/Modules/VisionFile.hpp index d9d379a9..8c43572e 100644 --- a/src/Components/Modules/VisionFile.hpp +++ b/src/Components/Modules/VisionFile.hpp @@ -8,12 +8,12 @@ namespace Components VisionFile(); private: - static const char* DvarExceptions[]; + static std::vector DvarExceptions; + static std::unordered_map VisionReplacements; - static void ApplyExemptDvar(const std::string& dvarName, const char* buffer, const std::string& fileName); - static void ApplyValueToSettings(const std::string& dvarName, const char* buffer, const std::string& fileName, Game::visionSetVars_t* settings); + static bool ApplyExemptDvar(const char* dvarName, const char** buffer, const char* filename); - static bool LoadVisionSettingsFromBuffer(const char* buffer, const char* fileName, Game::visionSetVars_t* settings); + static bool LoadVisionSettingsFromBuffer(const char* buffer, const char* filename, Game::visionSetVars_t* settings); static bool LoadVisionSettingsFromBuffer_Stub(); }; } diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index 1add1496..edcada03 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -4,12 +4,12 @@ namespace Components { int Zones::ZoneVersion; - int Zones::EntitiesVersion; int Zones::FxEffectIndex; char* Zones::FxEffectStrings[64]; - static std::unordered_map shellshock_replace_list = { + static std::unordered_map shellshock_replace_list = + { { "66","bg_shock_screenType" }, { "67","bg_shock_screenBlurBlendTime"}, { "68","bg_shock_screenBlurBlendFadeTime"}, @@ -41,26 +41,6 @@ namespace Components { "92","bg_shock_movement"} }; - static std::unordered_map vision_replace_list = { - { "511","r_glow" }, - { "516","r_glowRadius0" }, - { "512","r_glowBloomCutoff" }, - { "513","r_glowBloomDesaturation" }, - { "514","r_glowBloomIntensity0" }, - { "520","r_filmEnable" }, - { "522","r_filmContrast" }, - { "521","r_filmBrightness" }, - { "523","r_filmDesaturation" }, - { "524","r_filmDesaturationDark" }, - { "525","r_filmInvert" }, - { "526","r_filmLightTint" }, - { "527","r_filmMediumTint" }, - { "528","r_filmDarkTint" }, - { "529","r_primaryLightUseTweaks" }, - { "530","r_primaryLightTweakDiffuseStrength" }, - { "531","r_primaryLightTweakSpecularStrength" }, - }; - Game::XAssetType currentAssetType = Game::XAssetType::ASSET_TYPE_INVALID; Game::XAssetType previousAssetType = Game::XAssetType::ASSET_TYPE_INVALID; @@ -2559,8 +2539,6 @@ namespace Components int Zones::LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size) { - EntitiesVersion = Zones::Version(); - if (Zones::Version() >= 446) { size /= 44; @@ -3487,18 +3465,6 @@ namespace Components Game::DB_PopStreamPos(); } - - char* Zones::ParseVision_Stub(const char** data_p) - { - auto token = Game::Com_Parse(data_p); - - if (vision_replace_list.find(token) != vision_replace_list.end()) - { - return vision_replace_list[token].data(); - } - - return token; - } char* Zones::ParseShellShock_Stub(const char** data_p) { @@ -3507,10 +3473,10 @@ namespace Components { return shellshock_replace_list[token].data(); } + return token; } - Zones::Zones() { Zones::ZoneVersion = 0; @@ -3682,10 +3648,6 @@ namespace Components Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick(); Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick(); - // Fix newer vision file - Utils::Hook(0x59A849, ParseVision_Stub, HOOK_CALL).install()->quick(); - Utils::Hook(0x59A8AD, ParseVision_Stub, HOOK_CALL).install()->quick(); - #ifdef DEBUG // Easy dirty disk debugging Utils::Hook::Set(0x4CF7F0, 0xC3CC); diff --git a/src/Components/Modules/Zones.hpp b/src/Components/Modules/Zones.hpp index 615bc450..5c4899b0 100644 --- a/src/Components/Modules/Zones.hpp +++ b/src/Components/Modules/Zones.hpp @@ -21,14 +21,11 @@ namespace Components static void SetVersion(int version); - static int Version() { return Zones::ZoneVersion; }; - - static int GetEntitiesZoneVersion() { return Zones::EntitiesVersion; }; + static int Version() { return Zones::ZoneVersion; } private: static int ZoneVersion; - static int EntitiesVersion; static int FxEffectIndex; static char* FxEffectStrings[64]; @@ -105,6 +102,5 @@ namespace Components static void LoadTracerDefFxEffect(); static void FixImageCategory(Game::GfxImage* image); static char* ParseShellShock_Stub(const char** data_p); - static char* ParseVision_Stub(const char** data_p); }; } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 566baddd..d8932b76 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -995,7 +995,6 @@ namespace Game MaterialShaderArgument *args; }; - /* 9045 */ struct visionSetVars_t { bool glowEnable; diff --git a/src/Utils/String.cpp b/src/Utils/String.cpp index 223334a7..55cd12a0 100644 --- a/src/Utils/String.cpp +++ b/src/Utils/String.cpp @@ -43,6 +43,14 @@ namespace Utils return text; } + bool Compare(const std::string& lhs, const std::string& rhs) + { + return std::ranges::equal(lhs, rhs, [](const unsigned char a, const unsigned char b) + { + return std::tolower(a) == std::tolower(b); + }); + } + std::string DumpHex(const std::string& data, const std::string& separator) { std::string result; @@ -106,18 +114,18 @@ namespace Utils return std::equal(needle.rbegin(), needle.rend(), haystack.rbegin()); } - int IsSpace(int c) + bool IsNumber(const std::string& str) { - if (c < -1) return 0; - return _isspace_l(c, nullptr); + return !str.empty() && std::find_if(str.begin(), + str.end(), [](unsigned char input) { return !std::isdigit(input); }) == str.end(); } // Trim from start std::string& LTrim(std::string& str) { - str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](int val) + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](const unsigned char input) { - return !IsSpace(val); + return !std::isspace(input); })); return str; @@ -126,9 +134,9 @@ namespace Utils // Trim from end std::string& RTrim(std::string& str) { - str.erase(std::find_if(str.rbegin(), str.rend(), [](int val) + str.erase(std::find_if(str.rbegin(), str.rend(), [](const unsigned char input) { - return !IsSpace(val); + return !std::isspace(input); }).base(), str.end()); return str; diff --git a/src/Utils/String.hpp b/src/Utils/String.hpp index 90422c29..b6b376ba 100644 --- a/src/Utils/String.hpp +++ b/src/Utils/String.hpp @@ -4,7 +4,7 @@ namespace Utils { namespace String { - template + template class VAProvider { public: @@ -25,7 +25,7 @@ namespace Utils while (true) { - const auto res = _vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); + const auto res = vsnprintf_s(entry->buffer, entry->size, _TRUNCATE, format, ap); if (res > 0) break; // Success if (res == 0) return ""; // Error @@ -47,15 +47,15 @@ namespace Utils ~Entry() { - if (this->buffer) Utils::Memory::GetAllocator()->free(this->buffer); + if (this->buffer) Memory::GetAllocator()->free(this->buffer); this->size = 0; this->buffer = nullptr; } void allocate() { - if (this->buffer) Utils::Memory::GetAllocator()->free(this->buffer); - this->buffer = Utils::Memory::GetAllocator()->allocateArray(this->size + 1); + if (this->buffer) Memory::GetAllocator()->free(this->buffer); + this->buffer = Memory::GetAllocator()->allocateArray(this->size + 1); } void doubleSize() @@ -74,13 +74,14 @@ namespace Utils const char *VA(const char *fmt, ...); - int IsSpace(int c); std::string ToLower(std::string text); std::string ToUpper(std::string text); + bool Compare(const std::string& lhs, const std::string& rhs); std::vector Split(const std::string& str, char delim); void Replace(std::string& string, const std::string& find, const std::string& replace); bool StartsWith(const std::string& haystack, const std::string& needle); bool EndsWith(const std::string& haystack, const std::string& needle); + bool IsNumber(const std::string& str); std::string& LTrim(std::string& str); std::string& RTrim(std::string& str); @@ -94,7 +95,7 @@ namespace Utils std::string DumpHex(const std::string& data, const std::string& separator = " "); - std::string XOR(const std::string str, char value); + std::string XOR(std::string str, char value); std::string EncodeBase64(const char* input, const unsigned long inputSize); std::string EncodeBase64(const std::string& input); From 13d1c23b99efc7bdc30657490c5d35f3273aca39 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sat, 23 Jul 2022 23:22:58 +0200 Subject: [PATCH 69/77] [Script] *Update* --- src/Components/Modules/Bots.cpp | 12 +-- src/Components/Modules/Chat.cpp | 2 +- src/Components/Modules/ClientCommand.cpp | 50 +----------- src/Components/Modules/GSC/IO.cpp | 6 +- src/Components/Modules/GSC/Script.cpp | 77 +++---------------- src/Components/Modules/GSC/Script.hpp | 2 - .../Modules/GSC/ScriptExtension.cpp | 14 ++-- src/Components/Modules/GSC/ScriptStorage.cpp | 12 +-- src/Components/Modules/UserInfo.cpp | 8 +- src/Game/Structs.hpp | 2 +- 10 files changed, 41 insertions(+), 144 deletions(-) diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 77d041d8..72c3836b 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -119,10 +119,10 @@ namespace Components void Bots::AddMethods() { - Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self iw4x_IsBot(); - Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self iw4x_IsTestClient(); + Script::AddMethod("IsBot", Bots::GScr_isTestClient); // Usage: self IsBot(); + Script::AddMethod("IsTestClient", Bots::GScr_isTestClient); // Usage: self IsTestClient(); - Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: iw4x_BotStop(); + Script::AddMethod("BotStop", [](Game::scr_entref_t entref) // Usage: BotStop(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -137,7 +137,7 @@ namespace Components g_botai[entref.entnum].active = true; }); - Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: iw4x_BotWeapon(); + Script::AddMethod("BotWeapon", [](Game::scr_entref_t entref) // Usage: BotWeapon(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -160,7 +160,7 @@ namespace Components g_botai[entref.entnum].active = true; }); - Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: iw4x_BotAction(); + Script::AddMethod("BotAction", [](Game::scr_entref_t entref) // Usage: BotAction(); { const auto* ent = Game::GetPlayerEntity(entref); @@ -201,7 +201,7 @@ namespace Components Game::Scr_ParamError(0, "^1BotAction: Unknown action.\n"); }); - Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: iw4x_BotMovement(, ); + Script::AddMethod("BotMovement", [](Game::scr_entref_t entref) // Usage: BotMovement(, ); { const auto* ent = Game::GetPlayerEntity(entref); diff --git a/src/Components/Modules/Chat.cpp b/src/Components/Modules/Chat.cpp index b2f105e6..37c55e37 100644 --- a/src/Components/Modules/Chat.cpp +++ b/src/Components/Modules/Chat.cpp @@ -389,7 +389,7 @@ namespace Components void Chat::AddScriptFunctions() { - Script::AddFunction("OnPlayerSay", [] // gsc: iw4x_OnPlayerSay() + Script::AddFunction("OnPlayerSay", [] // gsc: OnPlayerSay() { if (Game::Scr_GetNumParam() != 1) { diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index cc552799..48077a44 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -351,49 +351,7 @@ namespace Components void ClientCommand::AddScriptFunctions() { - Script::AddMethod("Noclip", [](Game::scr_entref_t entref) // gsc: iw4x_Noclip(); - { - const auto* ent = Game::GetPlayerEntity(entref); - - if (Game::Scr_GetNumParam() >= 1) - { - if (Game::Scr_GetInt(0)) - { - ent->client->flags |= Game::PLAYER_FLAG_NOCLIP; - } - else - { - ent->client->flags &= ~Game::PLAYER_FLAG_NOCLIP; - } - } - else - { - ent->client->flags ^= Game::PLAYER_FLAG_NOCLIP; - } - }); - - Script::AddMethod("Ufo", [](Game::scr_entref_t entref) // gsc: iw4x_Ufo(); - { - const auto* ent = Game::GetPlayerEntity(entref); - - if (Game::Scr_GetNumParam() >= 1) - { - if (Game::Scr_GetInt(0)) - { - ent->client->flags |= Game::PLAYER_FLAG_UFO; - } - else - { - ent->client->flags &= ~Game::PLAYER_FLAG_UFO; - } - } - else - { - ent->client->flags ^= Game::PLAYER_FLAG_UFO; - } - }); - - Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: iw4x_God(); + Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(); { auto* ent = Game::GetEntity(entref); @@ -414,7 +372,7 @@ namespace Components } }); - Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: iw4x_Demigod(); + Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(); { auto* ent = Game::GetEntity(entref); @@ -435,7 +393,7 @@ namespace Components } }); - Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: iw4x_Notarget(); + Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(); { auto* ent = Game::GetEntity(entref); @@ -456,7 +414,7 @@ namespace Components } }); - Script::AddFunction("DropAllBots", [] // gsc: iw4x_DropAllBots(); + Script::AddFunction("DropAllBots", [] // gsc: DropAllBots(); { Game::SV_DropAllBots(); }); diff --git a/src/Components/Modules/GSC/IO.cpp b/src/Components/Modules/GSC/IO.cpp index 2eaa1203..159944eb 100644 --- a/src/Components/Modules/GSC/IO.cpp +++ b/src/Components/Modules/GSC/IO.cpp @@ -8,7 +8,7 @@ namespace Components void IO::AddScriptFunctions() { - Script::AddFunction("FileWrite", [] // gsc: iw4x_FileWrite(, , ) + Script::AddFunction("FileWrite", [] // gsc: FileWrite(, , ) { const auto* path = Game::Scr_GetString(0); auto* text = Game::Scr_GetString(1); @@ -53,7 +53,7 @@ namespace Components } }); - Script::AddFunction("FileRead", [] // gsc: iw4x_FileRead() + Script::AddFunction("FileRead", [] // gsc: FileRead() { const auto* path = Game::Scr_GetString(0); @@ -83,7 +83,7 @@ namespace Components Game::Scr_AddString(FileSystem::FileReader(scriptData).getBuffer().data()); }); - Script::AddFunction("FileExists", [] // gsc: iw4x_FileExists() + Script::AddFunction("FileExists", [] // gsc: FileExists() { const auto* path = Game::Scr_GetString(0); diff --git a/src/Components/Modules/GSC/Script.cpp b/src/Components/Modules/GSC/Script.cpp index 1ca8c235..683e1ede 100644 --- a/src/Components/Modules/GSC/Script.cpp +++ b/src/Components/Modules/GSC/Script.cpp @@ -6,33 +6,6 @@ namespace Components std::unordered_map Script::CustomScrFunctions; std::unordered_map Script::CustomScrMethods; - // This was added on the 17th of July 2022 to help transition current mods to - // the new prefixed functions. Look at the clock! If it's more than three months - // later than this date... remove this! - std::unordered_set Script::DeprecatedFunctionsAndMethods = - { - "isbot", - "istestclient", - "botstop", - "botweapon", - "botmovement", - "botaction", - "onplayersay", - "fileread", - "filewrite", - "fileexists", - "getsystemmilliseconds", - "exec", - "printconsole", - "arecontrolsfrozen", - "setping", - "setname", - "resetname", - "dropallbots", - "httpget", - "httpcancel" - }; - std::string Script::ScriptName; std::vector Script::ScriptNameStack; unsigned short Script::FunctionName; @@ -303,48 +276,28 @@ namespace Components void Script::AddFunction(const std::string& name, Game::BuiltinFunction func, bool type) { - const auto functionName = Script::ClientPrefix + name; - Script::ScriptFunction toAdd; toAdd.actionFunc = func; toAdd.type = type; - CustomScrFunctions.insert_or_assign(Utils::String::ToLower(functionName), toAdd); + CustomScrFunctions.insert_or_assign(Utils::String::ToLower(name), toAdd); } void Script::AddMethod(const std::string& name, Game::BuiltinMethod func, bool type) { - const auto functionName = Script::ClientPrefix + name; - Script::ScriptMethod toAdd; toAdd.actionFunc = func; toAdd.type = type; - CustomScrMethods.insert_or_assign(Utils::String::ToLower(functionName), toAdd); - } - - bool Script::IsDeprecated(const std::string& name) - { - return Script::DeprecatedFunctionsAndMethods.contains(name); + CustomScrMethods.insert_or_assign(Utils::String::ToLower(name), toAdd); } Game::BuiltinFunction Script::BuiltIn_GetFunctionStub(const char** pName, int* type) { if (pName != nullptr) { - auto name = Utils::String::ToLower(*pName); + const auto got = Script::CustomScrFunctions.find(Utils::String::ToLower(*pName)); - if (IsDeprecated(name)) - { - Toast::Show("cardicon_gumby", "WARNING!", std::format("{} uses the deprecated function {}", Script::ScriptName, name), 2048); - Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "*** DEPRECATION WARNING ***\n"); - Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated builtin {} from {}! This method or function should be prefixed with '{}'. Please update your mod!\n", name, Script::ScriptName, Script::ClientPrefix); - Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "***************************\n"); - - name = Script::ClientPrefix + name; // Fixes it automatically - } - - const auto got = Script::CustomScrFunctions.find(name); // If no function was found let's call game's function if (got != Script::CustomScrFunctions.end()) { @@ -367,20 +320,8 @@ namespace Components { if (pName != nullptr) { - auto name = Utils::String::ToLower(*pName); + const auto got = Script::CustomScrMethods.find(Utils::String::ToLower(*pName)); - if (IsDeprecated(name)) - { - Toast::Show("cardicon_gumby", "WARNING!", std::format("{} uses the deprecated method {}", Script::ScriptName, name), 2048); - Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "*** DEPRECATION WARNING ***\n"); - Logger::PrintError(Game::CON_CHANNEL_ERROR, "Attempted to execute deprecated builtin {} from {}! This function or method should be prefixed with '{}'. Please update your mod!\n", name, Script::ScriptName, Script::ClientPrefix); - Logger::Print(Game::CON_CHANNEL_PARSERSCRIPT, "***************************\n"); - - name = Script::ClientPrefix + name; // Fixes it automatically - - } - - const auto got = Script::CustomScrMethods.find(name); // If no method was found let's call game's function if (got != Script::CustomScrMethods.end()) { @@ -596,7 +537,7 @@ namespace Components void Script::AddFunctions() { - Script::AddFunction("ReplaceFunc", [] // gsc: iw4x_ReplaceFunc(, ) + Script::AddFunction("ReplaceFunc", [] // gsc: ReplaceFunc(, ) { if (Game::Scr_GetNumParam() != 2) { @@ -611,7 +552,7 @@ namespace Components }); // System time - Script::AddFunction("GetSystemMilliseconds", [] // gsc: iw4x_GetSystemMilliseconds() + Script::AddFunction("GetSystemMilliseconds", [] // gsc: GetSystemMilliseconds() { SYSTEMTIME time; GetSystemTime(&time); @@ -620,7 +561,7 @@ namespace Components }); // Executes command to the console - Script::AddFunction("Exec", [] // gsc: iw4x_Exec() + Script::AddFunction("Exec", [] // gsc: Exec() { const auto str = Game::Scr_GetString(0); @@ -634,7 +575,7 @@ namespace Components }); // Allow printing to the console even when developer is 0 - Script::AddFunction("PrintConsole", [] // gsc: iw4x_PrintConsole() + Script::AddFunction("PrintConsole", [] // gsc: PrintConsole() { for (std::size_t i = 0; i < Game::Scr_GetNumParam(); ++i) { @@ -651,7 +592,7 @@ namespace Components }); // PlayerCmd_AreControlsFrozen GSC function from Black Ops 2 - Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self iw4x_AreControlsFrozen(); + Script::AddMethod("AreControlsFrozen", [](Game::scr_entref_t entref) // Usage: self AreControlsFrozen(); { const auto* ent = Game::GetPlayerEntity(entref); diff --git a/src/Components/Modules/GSC/Script.hpp b/src/Components/Modules/GSC/Script.hpp index f92d9f3a..0b585d94 100644 --- a/src/Components/Modules/GSC/Script.hpp +++ b/src/Components/Modules/GSC/Script.hpp @@ -27,10 +27,8 @@ namespace Components bool type; }; - static constexpr auto* ClientPrefix = "iw4x_"; static std::unordered_map CustomScrFunctions; static std::unordered_map CustomScrMethods; - static std::unordered_set DeprecatedFunctionsAndMethods; static std::string ScriptName; static std::vector ScriptNameStack; diff --git a/src/Components/Modules/GSC/ScriptExtension.cpp b/src/Components/Modules/GSC/ScriptExtension.cpp index 411282dc..fbcc8f06 100644 --- a/src/Components/Modules/GSC/ScriptExtension.cpp +++ b/src/Components/Modules/GSC/ScriptExtension.cpp @@ -116,7 +116,7 @@ namespace Components void ScriptExtension::AddFunctions() { // Misc functions - Script::AddFunction("ToUpper", [] // gsc: iw4x_ToUpper() + Script::AddFunction("ToUpper", [] // gsc: ToUpper() { const auto scriptValue = Game::Scr_GetConstString(0); const auto* string = Game::SL_ConvertToString(scriptValue); @@ -161,7 +161,7 @@ namespace Components }); // Func present on IW5 - Script::AddFunction("StrICmp", [] // gsc: iw4x_StrICmp(, ) + Script::AddFunction("StrICmp", [] // gsc: StrICmp(, ) { const auto value1 = Game::Scr_GetConstString(0); const auto value2 = Game::Scr_GetConstString(1); @@ -173,7 +173,7 @@ namespace Components }); // Func present on IW5 - Script::AddFunction("IsEndStr", [] // gsc: iw4x_IsEndStr(, ) + Script::AddFunction("IsEndStr", [] // gsc: IsEndStr(, ) { const auto* s1 = Game::Scr_GetString(0); const auto* s2 = Game::Scr_GetString(1); @@ -187,7 +187,7 @@ namespace Components Game::Scr_AddBool(Utils::String::EndsWith(s1, s2)); }); - Script::AddFunction("IsArray", [] // gsc: iw4x_IsArray() + Script::AddFunction("IsArray", [] // gsc: IsArray() { auto type = Game::Scr_GetType(0); @@ -211,7 +211,7 @@ namespace Components void ScriptExtension::AddMethods() { // ScriptExtension methods - Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self iw4x_GetIp() + Script::AddMethod("GetIp", [](Game::scr_entref_t entref) // gsc: self GetIp() { const auto* ent = Game::GetPlayerEntity(entref); const auto* client = Script::GetClient(ent); @@ -224,7 +224,7 @@ namespace Components Game::Scr_AddString(ip.data()); }); - Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self iw4x_GetPing() + Script::AddMethod("GetPing", [](Game::scr_entref_t entref) // gsc: self GetPing() { const auto* ent = Game::GetPlayerEntity(entref); const auto* client = Script::GetClient(ent); @@ -232,7 +232,7 @@ namespace Components Game::Scr_AddInt(client->ping); }); - Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self iw4x_SetPing() + Script::AddMethod("SetPing", [](Game::scr_entref_t entref) // gsc: self SetPing() { auto ping = Game::Scr_GetInt(0); diff --git a/src/Components/Modules/GSC/ScriptStorage.cpp b/src/Components/Modules/GSC/ScriptStorage.cpp index 8a1c47c1..b169d1ea 100644 --- a/src/Components/Modules/GSC/ScriptStorage.cpp +++ b/src/Components/Modules/GSC/ScriptStorage.cpp @@ -8,7 +8,7 @@ namespace Components void ScriptStorage::AddScriptFunctions() { - Script::AddFunction("StorageSet", [] // gsc: iw4x_StorageSet(, ); + Script::AddFunction("StorageSet", [] // gsc: StorageSet(, ); { const auto* key = Game::Scr_GetString(0); const auto* value = Game::Scr_GetString(1); @@ -22,7 +22,7 @@ namespace Components Data.insert_or_assign(key, value); }); - Script::AddFunction("StorageRemove", [] // gsc: iw4x_StorageRemove(); + Script::AddFunction("StorageRemove", [] // gsc: StorageRemove(); { const auto* key = Game::Scr_GetString(0); @@ -41,7 +41,7 @@ namespace Components Data.erase(key); }); - Script::AddFunction("StorageGet", [] // gsc: iw4x_StorageGet(); + Script::AddFunction("StorageGet", [] // gsc: StorageGet(); { const auto* key = Game::Scr_GetString(0); @@ -61,7 +61,7 @@ namespace Components Game::Scr_AddString(data.data()); }); - Script::AddFunction("StorageHas", [] // gsc: iw4x_StorageHas(); + Script::AddFunction("StorageHas", [] // gsc: StorageHas(); { const auto* key = Game::Scr_GetString(0); @@ -74,7 +74,7 @@ namespace Components Game::Scr_AddBool(Data.contains(key)); }); - Script::AddFunction("StorageDump", [] // gsc: iw4x_StorageDump(); + Script::AddFunction("StorageDump", [] // gsc: StorageDump(); { if (Data.empty()) { @@ -87,7 +87,7 @@ namespace Components FileSystem::FileWriter("scriptdata/scriptstorage.json").write(json.dump()); }); - Script::AddFunction("StorageClear", [] // gsc: iw4x_StorageClear(); + Script::AddFunction("StorageClear", [] // gsc: StorageClear(); { Data.clear(); }); diff --git a/src/Components/Modules/UserInfo.cpp b/src/Components/Modules/UserInfo.cpp index 39b6cd63..ab425dda 100644 --- a/src/Components/Modules/UserInfo.cpp +++ b/src/Components/Modules/UserInfo.cpp @@ -44,7 +44,7 @@ namespace Components void UserInfo::AddScriptMethods() { - Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self iw4x_SetName() + Script::AddMethod("SetName", [](Game::scr_entref_t entref) // gsc: self SetName() { const auto* ent = Game::GetPlayerEntity(entref); const auto* name = Game::Scr_GetString(0); @@ -60,7 +60,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self iw4x_ResetName() + Script::AddMethod("ResetName", [](Game::scr_entref_t entref) // gsc: self ResetName() { const auto* ent = Game::GetPlayerEntity(entref); @@ -69,7 +69,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self iw4x_setClanTag() + Script::AddMethod("SetClanTag", [](Game::scr_entref_t entref) // gsc: self SetClanTag() { const auto* ent = Game::GetPlayerEntity(entref); const auto* clanName = Game::Scr_GetString(0); @@ -85,7 +85,7 @@ namespace Components Game::ClientUserinfoChanged(ent->s.number); }); - Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self iw4x_ResetClanTag() + Script::AddMethod("ResetClanTag", [](Game::scr_entref_t entref) // gsc: self ResetClanTag() { const auto* ent = Game::GetPlayerEntity(entref); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index c57e84f6..0d6b250b 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -6246,7 +6246,7 @@ namespace Game static_assert(sizeof(gentity_s) == 0x274); - enum $1C4253065710F064DA9E4D59ED6EC544 + enum { ENTFIELD_ENTITY = 0x0, ENTFIELD_SENTIENT = 0x2000, From 725c08afebdbe51aec8c79844a8f234a75fcee49 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 24 Jul 2022 01:08:01 +0200 Subject: [PATCH 70/77] [Script] Remove builtin God, demigod, notarget --- src/Components/Modules/ClientCommand.cpp | 69 ++---------------------- src/Components/Modules/ClientCommand.hpp | 4 +- src/Game/Structs.hpp | 2 +- 3 files changed, 6 insertions(+), 69 deletions(-) diff --git a/src/Components/Modules/ClientCommand.cpp b/src/Components/Modules/ClientCommand.cpp index 48077a44..fb052c94 100644 --- a/src/Components/Modules/ClientCommand.cpp +++ b/src/Components/Modules/ClientCommand.cpp @@ -26,11 +26,11 @@ namespace Components return true; } - void ClientCommand::Add(const char* name, std::function callback) + void ClientCommand::Add(const char* name, const std::function& callback) { const auto command = Utils::String::ToLower(name); - ClientCommand::HandlersSV[command] = std::move(callback); + ClientCommand::HandlersSV[command] = callback; } void ClientCommand::ClientCommandStub(const int clientNum) @@ -52,7 +52,7 @@ namespace Components return; } - Utils::Hook::Call(0x416790)(clientNum); + Utils::Hook::Call(0x416790)(clientNum); } void ClientCommand::AddCheatCommands() @@ -351,69 +351,6 @@ namespace Components void ClientCommand::AddScriptFunctions() { - Script::AddMethod("God", [](Game::scr_entref_t entref) // gsc: God(); - { - auto* ent = Game::GetEntity(entref); - - if (Game::Scr_GetNumParam() >= 1) - { - if (Game::Scr_GetInt(0)) - { - ent->flags |= Game::FL_GODMODE; - } - else - { - ent->flags &= ~Game::FL_GODMODE; - } - } - else - { - ent->flags ^= Game::FL_GODMODE; - } - }); - - Script::AddMethod("Demigod", [](Game::scr_entref_t entref) // gsc: Demigod(); - { - auto* ent = Game::GetEntity(entref); - - if (Game::Scr_GetNumParam() >= 1) - { - if (Game::Scr_GetInt(0)) - { - ent->flags |= Game::FL_DEMI_GODMODE; - } - else - { - ent->flags &= ~Game::FL_DEMI_GODMODE; - } - } - else - { - ent->flags ^= Game::FL_DEMI_GODMODE; - } - }); - - Script::AddMethod("Notarget", [](Game::scr_entref_t entref) // gsc: Notarget(); - { - auto* ent = Game::GetEntity(entref); - - if (Game::Scr_GetNumParam() >= 1) - { - if (Game::Scr_GetInt(0)) - { - ent->flags |= Game::FL_NOTARGET; - } - else - { - ent->flags &= ~Game::FL_NOTARGET; - } - } - else - { - ent->flags ^= Game::FL_NOTARGET; - } - }); - Script::AddFunction("DropAllBots", [] // gsc: DropAllBots(); { Game::SV_DropAllBots(); diff --git a/src/Components/Modules/ClientCommand.hpp b/src/Components/Modules/ClientCommand.hpp index 984b597f..60019358 100644 --- a/src/Components/Modules/ClientCommand.hpp +++ b/src/Components/Modules/ClientCommand.hpp @@ -7,13 +7,13 @@ namespace Components public: ClientCommand(); - static void Add(const char* name, 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 void ClientCommandStub(const int clientNum); + static void ClientCommandStub(int clientNum); static void AddCheatCommands(); static void AddDevelopmentCommands(); static void AddScriptFunctions(); diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index 0d6b250b..efe17a76 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -296,7 +296,7 @@ namespace Game CON_BUILTIN_CHANNEL_COUNT, }; - enum entityFlag + enum { FL_GODMODE = 0x1, FL_DEMI_GODMODE = 0x2, From 0d530ef36e7ae94170970d1d78fe1193c12bbcc7 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 24 Jul 2022 12:37:58 +0200 Subject: [PATCH 71/77] [Structs] Use binary left shift in enum --- src/Game/Structs.hpp | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index efe17a76..af92e04d 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -298,24 +298,24 @@ namespace Game enum { - FL_GODMODE = 0x1, - FL_DEMI_GODMODE = 0x2, - FL_NOTARGET = 0x4, - FL_NO_KNOCKBACK = 0x8, - FL_NO_RADIUS_DAMAGE = 0x10, - FL_SUPPORTS_LINKTO = 0x1000, - FL_NO_AUTO_ANIM_UPDATE = 0x2000, - FL_GRENADE_TOUCH_DAMAGE = 0x4000, - FL_STABLE_MISSILES = 0x20000, - FL_REPEAT_ANIM_UPDATE = 0x40000, - FL_VEHICLE_TARGET = 0x80000, - FL_GROUND_ENT = 0x100000, - FL_CURSOR_HINT = 0x200000, - FL_MISSILE_ATTRACTOR = 0x800000, - FL_WEAPON_BEING_GRABBED = 0x1000000, - FL_DELETE = 0x2000000, - FL_BOUNCE = 0x4000000, - FL_MOVER_SLIDE = 0x8000000 + FL_GODMODE = 1 << 0, + FL_DEMI_GODMODE = 1 << 1, + FL_NOTARGET = 1 << 2, + FL_NO_KNOCKBACK = 1 << 3, + FL_NO_RADIUS_DAMAGE = 1 << 4, + FL_SUPPORTS_LINKTO = 1 << 12, + FL_NO_AUTO_ANIM_UPDATE = 1 << 13, + FL_GRENADE_TOUCH_DAMAGE = 1 << 14, + FL_STABLE_MISSILES = 1 << 17, + FL_REPEAT_ANIM_UPDATE = 1 << 18, + FL_VEHICLE_TARGET = 1 << 19, + FL_GROUND_ENT = 1 << 20, + FL_CURSOR_HINT = 1 << 21, + FL_MISSILE_ATTRACTOR = 1 << 23, + FL_WEAPON_BEING_GRABBED = 1 << 24, + FL_DELETE = 1 << 25, + FL_BOUNCE = 1 << 26, + FL_MOVER_SLIDE = 1 << 27 }; enum ClassNum : unsigned int From b787ba7e7f91cda95fd0accf10d84dd66708ca37 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 24 Jul 2022 13:06:23 +0200 Subject: [PATCH 72/77] [Changelog] v0.7.4 --- .github/pull_request_template.md | 2 +- CHANGELOG.md | 46 +++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 512b32c9..82a087e1 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ Thanks for the contribution! Please provide a concise description of the problem this request solves. -**How does this PR change IW4x's behavior?** +**How does this PR change IW4x's behaviour?** Are there any breaking changes? Will any existing behaviour change? Specify the new expected behaviour if this PR adds a new gameplay feature or alters an existing game mechanic. You may provide an image or video showing the changes. diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c1a49aa..04e9ff26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,46 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [0.7.4] - 2022-07-25 + +### Added + +- Add `DisableWeaponPickup` GSC method (#329) +- Add `EnableWeaponPickup` GSC method (#329) +- Add `protect-saved-dvars` command line argument (#335) +- Add `clanName` dvar. Can be edited in the `barracks` menu (#361) +- Add DLC9 containing classic maps from CoD4: backlot, Chinatown, Winter Crash, Pipeline and Downpour + +### Changed + +- `sv_mapRotationCurrent` functionality has been restored for backwards compatibility (#328) +- GSC IO Functions are restricted to the `scriptdata` folder (#322) +- `scriptablehttp` command line argument is no longer needed (#322) +- Removed `noclip` built-in GSC method. Replaced with script-based solution (#387) +- Removed `ufo` built-in GSC method. Replaced with script-based solution (#387) +- Removed `god` built-in GSC method. Replaced with script-based solution (#388) +- Removed `demiGod` built-in GSC method. Replaced with script-based solution (#388) +- Removed `noTarget` built-in GSC method. Replaced with script-based solution (#388) +- Added to the iw4x-rawfiles `common_scripts\iw4x_utility` GSC script, it contains the scripts-based solution for the removed GSC built-in methods + +### Fixed + +- Node system works again for clients (#331) +- Fixed output of `g_log_list` & `log_list` (#336) +- Fixed toast notifications (#337) +- Fixed `banClient` command (#311) +- Fixed singleplayer maps crashing in renderer module (#340) +- Fixed muzzle flash on COD:OL maps (#352) +- Server commands are no longer being registered twice (#339) +- Fixed issue where grenades and projectiles had no bounce sounds (#368) +- Fixed issue that could cause the game to crash when loading CoD4 maps (#372) + +### Known issues + +- HTTPS is not supported for fast downloads at the moment. +- Sound issue fix is experimental as the bug is not fully understood. +- `reloadmenus` command does not free resources used by custom menus. + ## [0.7.3] - 2022-06-23 ### Added @@ -27,7 +67,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. - Fix crash on clearing key binds (#278) - Fix maps dropping out of the map rotation when using `sv_randomMapRotation` (#283) -### Known issue +### Known issues - HTTPS is not supported for fast downloads at the moment. - Sound issue fix is experimental as the bug is not fully understood. @@ -51,7 +91,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. - Fixed internet, local and favorites filters (#260) - `sv_lanOnly` Dvar now prevents the server from sending heartbeats to master if set to true (#246) -### Known issue +### Known issues - HTTPS is not supported for fast downloads at the moment. - Sound issue fix is experimental as the bug is not fully understood. @@ -85,7 +125,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. - Fixed client crash when cg_chatHeight was set to 0 (#237) - Fixed GSC function Scr_TableLookupIStringByRow (#162) -### Known issue +### Known issues - HTTPS is not supported for fast downloads at the moment. - Sound issue fix is experimental as the bug is not fully understood. From e330c51fc02695feae57489501cc61d2f918de50 Mon Sep 17 00:00:00 2001 From: Diavolo Date: Sun, 24 Jul 2022 13:18:46 +0200 Subject: [PATCH 73/77] [General] Create config.yml for issues templates --- .github/ISSUE_TEMPLATE/config.yml | 8 ++++++++ .github/ISSUE_TEMPLATE/get-help.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/config.yml diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..bfa69ba1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Discord Server + url: https://discord.gg/sKeVmR3/ + about: Please ask and answer support questions here. + - name: XLabsProject Website + url: https://xlabs.dev/ + about: The official website. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/get-help.md b/.github/ISSUE_TEMPLATE/get-help.md index 2455e886..b1e29541 100644 --- a/.github/ISSUE_TEMPLATE/get-help.md +++ b/.github/ISSUE_TEMPLATE/get-help.md @@ -23,7 +23,7 @@ If IW4x is crashing, include the minidump file and the crash address in text for **What have you tried so far?** Describe any steps you've already taken to try to get past this issue. Have you found a workaround? -**What version of Premake are you using?** +**What version of IW4x are you using?** `iw4x.exe -version` will show you the version. Please make sure you are up to date with the latest master branch. **Anything else we should know?** From 62a67d336f36c8e603fc822acc449e2c48a2a761 Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Tue, 26 Jul 2022 14:08:28 +0200 Subject: [PATCH 74/77] Fix crash snow and use disk models by default (#390) Co-authored-by: rackover --- src/Components/Modules/AssetInterfaces/IMaterial.cpp | 1 + src/Components/Modules/ZoneBuilder.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 743fbfa5..c594f970 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -44,6 +44,7 @@ namespace Assets {"wc_unlit_alphatest", "wc_unlit_blend_lin"}, {"wc_unlit_blend", "wc_unlit_blend_lin_ua"}, {"wc_unlit_replace", "wc_unlit_replace_lin"}, + {"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" }, {"mc_unlit_replace", "mc_unlit_replace_lin"}, {"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"}, diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 2053d21a..3bc00b25 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -1527,7 +1527,8 @@ namespace Components Logger::Print("------------------- END IWI DUMP -------------------\n"); }); - ZoneBuilder::PreferDiskAssetsDvar = Dvar::Register("zb_prefer_disk_assets", false, Game::DVAR_NONE, "Should zonebuilder prefer in-memory assets (requirements) or disk assets, when both are present?"); + // True by default, but can be put to zero for backward compatibility if needed + ZoneBuilder::PreferDiskAssetsDvar = Dvar::Register("zb_prefer_disk_assets", true, Game::DVAR_NONE, "Should zonebuilder prefer in-memory assets (requirements) or disk assets, when both are present?"); } } From 14181d04d16e6c38ffdf2da4eeb796f7780b4c61 Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Wed, 27 Jul 2022 10:54:36 +0200 Subject: [PATCH 75/77] =?UTF-8?q?Require=20teamzones=20when=20there's=20no?= =?UTF-8?q?=20arena=20entry=20(fixes=20sp=20maps=20missing=20=E2=80=A6=20(?= =?UTF-8?q?#391)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Require teamzones when there's no arena entry (fixes sp maps missing teams) * Move requireteamzones=true to method Co-authored-by: rackover --- src/Components/Modules/Maps.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index 54931bc8..39ef37c3 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -340,11 +340,17 @@ namespace Components Maps::MapDependencies dependencies{}; + // True by default - cause some maps won't have an arenafile entry + dependencies.requiresTeamZones = true; + for (int i = 0; i < *Game::arenaCount; ++i) { Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; if (arena->mapName == map) { + // If it's in the arena file, surely it's a vanilla map that doesn't need teams... + dependencies.requiresTeamZones = false; + for (std::size_t j = 0; j < std::extent_v; ++j) { const auto* key = arena->keys[j]; @@ -363,6 +369,7 @@ namespace Components } else if (key == "useteamzones"s) { + // ... unless it specifies so! This allows loading of CODO/COD4 zones that might not have the correct teams dependencies.requiresTeamZones = Utils::String::ToLower(value) == "true"s; } } From 088b8ac045bf9cf0373121479621eb1dd955337f Mon Sep 17 00:00:00 2001 From: Louve <33836535+Rackover@users.noreply.github.com> Date: Wed, 27 Jul 2022 17:06:11 +0200 Subject: [PATCH 76/77] Use correct DB thread for structured data loading (#392) Co-authored-by: rackover --- src/Components/Modules/StructuredData.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Components/Modules/StructuredData.cpp b/src/Components/Modules/StructuredData.cpp index ff938b9f..58184eb0 100644 --- a/src/Components/Modules/StructuredData.cpp +++ b/src/Components/Modules/StructuredData.cpp @@ -198,7 +198,8 @@ namespace Components // First check if all versions are present for (int i = 156;; ++i) { - FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i)); + // We're on DB thread (OnLoad) so use DB thread for FS + FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i), Game::FsThread::FS_THREAD_DATABASE); if (!definition.exists()) break; std::vector> enumContainer; From fb63f8a68172bb7e0968df616cecd147498cf54d Mon Sep 17 00:00:00 2001 From: Edo Date: Thu, 28 Jul 2022 14:51:00 +0200 Subject: [PATCH 77/77] Update CHANGELOG.md --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04e9ff26..424f3859 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/). -## [0.7.4] - 2022-07-25 +## [0.7.4] - 2022-07-28 ### Added @@ -12,7 +12,7 @@ The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0. - Add `EnableWeaponPickup` GSC method (#329) - Add `protect-saved-dvars` command line argument (#335) - Add `clanName` dvar. Can be edited in the `barracks` menu (#361) -- Add DLC9 containing classic maps from CoD4: backlot, Chinatown, Winter Crash, Pipeline and Downpour +- Add DLC9 containing classic maps from CoD4: Backlot, Chinatown, Winter Crash, Pipeline and Downpour ### Changed