diff --git a/src/client/component/dvars_patches.cpp b/src/client/component/dvars_patches.cpp new file mode 100644 index 00000000..45047310 --- /dev/null +++ b/src/client/component/dvars_patches.cpp @@ -0,0 +1,82 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/utils.hpp" +#include "scheduler.hpp" + +#include + +namespace dvars_patches +{ + namespace + { + void patch_dvars() + { + if (game::is_server()) + { + game::register_dvar_bool("com_pauseSupported", false, game::DVAR_NONE, "Whether is pause is ever supported by the game mode"); + } + } + + void patch_flags() + { + game::dvar_set_flags("com_pauseSupported", game::DVAR_SERVERINFO); + + if (game::is_client()) + { + game::dvar_set_flags("r_dof_enable", game::DVAR_ARCHIVE); + game::dvar_set_flags("r_lodbiasrigid", game::DVAR_ARCHIVE); + game::dvar_set_flags("gpad_stick_deadzone_max", game::DVAR_ARCHIVE); + game::dvar_set_flags("gpad_stick_deadzone_min", game::DVAR_ARCHIVE); + } + + scheduler::execute(scheduler::pipeline::dvars_flags_patched); + } + + void dof_enabled_stub(utils::hook::assembler& a) + { + const auto update_ads_dof = a.newLabel(); + + a.pushad64(); + a.push(rax); + + a.mov(rax, qword_ptr(0x14AE95478_g)); // r_dof_enable + a.cmp(byte_ptr(rax, 0x28), 1); + + a.pop(rax); + a.je(update_ads_dof); + + a.popad64(); + a.jmp(0x141116ECB_g); + + a.bind(update_ads_dof); + a.lea(rdx, ptr(rbx, 0x131EB4)); + a.mov(ecx, esi); + a.call_aligned(0x141107EC0_g); // CG_UpdateAdsDof + + a.popad64(); + a.jmp(0x141116F49_g); + } + } + + class component final : public generic_component + { + public: + void post_unpack() override + { + patch_dvars(); + scheduler::once(patch_flags, scheduler::pipeline::main, 10s); + + if (game::is_server()) + { + return; + } + + // toggle ADS dof based on r_dof_enable + utils::hook::jump(0x141116EBB_g, utils::hook::assemble(dof_enabled_stub), true); + } + }; +} + +REGISTER_COMPONENT(dvars_patches::component) diff --git a/src/client/component/scheduler.hpp b/src/client/component/scheduler.hpp index ac5c3f06..8949b6e4 100644 --- a/src/client/component/scheduler.hpp +++ b/src/client/component/scheduler.hpp @@ -1,38 +1,41 @@ -#pragma once - -namespace scheduler -{ - enum pipeline - { - // Asynchronuous pipeline, disconnected from the game - async = 0, - - // The game's rendering pipeline - renderer, - - // The game's server thread - server, - - // The game's main thread +#pragma once + +namespace scheduler +{ + enum pipeline + { + // Asynchronuous pipeline, disconnected from the game + async = 0, + + // The game's rendering pipeline + renderer, + + // The game's server thread + server, + + // The game's main thread main, + // Dvars flags have been patched, ready to be set from config file + dvars_flags_patched, + // Dvars are done loading from the config file - dvars_loaded, - - count, - }; - - static const bool cond_continue = false; + dvars_loaded, + + count, + }; + + static const bool cond_continue = false; static const bool cond_end = true; - void execute(const pipeline type); - - void schedule(const std::function& callback, pipeline type = pipeline::async, - std::chrono::milliseconds delay = 0ms); - void loop(const std::function& callback, pipeline type = pipeline::async, - std::chrono::milliseconds delay = 0ms); - void once(const std::function& callback, pipeline type = pipeline::async, - std::chrono::milliseconds delay = 0ms); - void on_game_initialized(const std::function& callback, pipeline type = pipeline::async, - std::chrono::milliseconds delay = 0ms); -} + void execute(const pipeline type); + + void schedule(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void loop(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void once(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); + void on_game_initialized(const std::function& callback, pipeline type = pipeline::async, + std::chrono::milliseconds delay = 0ms); +} diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index f420fd68..ff759ff0 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -646,10 +646,11 @@ namespace game enum dvarFlags_e { + DVAR_NONE = 0, DVAR_ARCHIVE = 1 << 0, DVAR_USERINFO = 1 << 1, - DVAR_SYSTEMINFO = 1 << 2, - DVAR_CODINFO = 1 << 3, + DVAR_SERVERINFO = 1 << 2, + DVAR_SYSTEMINFO = 1 << 3, DVAR_LATCH = 1 << 4, DVAR_ROM = 1 << 5, DVAR_SAVED = 1 << 6, diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 741a07e5..a97fa2be 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -106,7 +106,7 @@ namespace game WEAK symbol Dvar_GetBool{0x1422BCED0}; WEAK symbol Dvar_RegisterBool{ - 0x1422D0900 + 0x1422D0900, 0x14057B500 }; WEAK symbol Dvar_RegisterString{ @@ -116,6 +116,9 @@ namespace game WEAK symbol Dvar_SetFromStringByName{ 0x1422C7500 }; + WEAK symbol Dvar_GetSessionModeSpecificDvar{ + 0x1422BF500, 0x140575D90 + }; // UI WEAK symbol UI_CoD_Init{0x141F29010, 0x1404A0A50}; diff --git a/src/client/game/utils.cpp b/src/client/game/utils.cpp index 143c73f4..da6401d7 100644 --- a/src/client/game/utils.cpp +++ b/src/client/game/utils.cpp @@ -26,4 +26,68 @@ namespace game return dvar->current.value.integer; } + + bool get_dvar_bool(const char* dvar_name) + { + const auto dvar = Dvar_FindVar(dvar_name); + if (!dvar) + { + return {}; + } + + return dvar->current.value.enabled; + } + + dvar_t* register_dvar_bool(const char* dvar_name, const bool value, const dvarFlags_e flags, const char* description) + { + const auto hash = Dvar_GenerateHash(dvar_name); + auto registered_dvar = Dvar_RegisterBool(hash, dvar_name, value, flags, description); + + if (registered_dvar) + { + registered_dvar->debugName = dvar_name; + } + + return registered_dvar; + } + + void dvar_add_flags(const char* dvar_name, const dvarFlags_e flags) + { + auto dvar = Dvar_FindVar(dvar_name); + + if (!dvar) + { + return; + } + + auto dvar_to_change = dvar; + + if (dvar_to_change->type == DVAR_TYPE_SESSIONMODE_BASE_DVAR) + { + const auto mode = Com_SessionMode_GetMode(); + dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, mode); + } + + dvar_to_change->flags |= flags; + } + + void dvar_set_flags(const char* dvar_name, const dvarFlags_e flags) + { + auto dvar = Dvar_FindVar(dvar_name); + + if (!dvar) + { + return; + } + + auto dvar_to_change = dvar; + + if (dvar_to_change->type == DVAR_TYPE_SESSIONMODE_BASE_DVAR) + { + const auto mode = Com_SessionMode_GetMode(); + dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, mode); + } + + dvar_to_change->flags = flags; + } } diff --git a/src/client/game/utils.hpp b/src/client/game/utils.hpp index 5bd1a9a4..bcb1d2a2 100644 --- a/src/client/game/utils.hpp +++ b/src/client/game/utils.hpp @@ -1,7 +1,14 @@ #pragma once +#include "structs.hpp" + namespace game { std::string get_dvar_string(const char* dvar_name); int get_dvar_int(const char* dvar_name); + bool get_dvar_bool(const char* dvar_name); + + dvar_t* register_dvar_bool(const char* dvar_name, bool value, dvarFlags_e flags, const char* description); + void dvar_add_flags(const char* dvar, dvarFlags_e flags); + void dvar_set_flags(const char* dvar_name, dvarFlags_e flags); }