From 079f90631ccebf36c2f25c1d5dc27c8ba1a04a76 Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Fri, 20 Dec 2024 17:07:54 +0100 Subject: [PATCH 1/5] December 2069 Update --- deps/GSL | 2 +- deps/WinToast | 2 +- deps/asmjit | 2 +- deps/gsc-tool | 2 +- deps/libtomcrypt | 2 +- deps/libtommath | 2 +- deps/minhook | 2 +- deps/rapidjson | 2 +- deps/zlib | 2 +- src/client/component/auth.cpp | 27 ++- src/client/component/bullet.cpp | 4 +- src/client/component/command.cpp | 5 +- src/client/component/dedicated.cpp | 4 +- src/client/component/dedicated_info.cpp | 12 +- src/client/component/demonware.cpp | 2 +- src/client/component/dvar_cheats.cpp | 2 +- src/client/component/dvars.cpp | 28 +-- src/client/component/fastfiles.cpp | 2 +- src/client/component/filesystem.cpp | 5 + src/client/component/fps.cpp | 10 +- src/client/component/game_console.cpp | 21 +- src/client/component/game_log.cpp | 6 +- src/client/component/gameplay.cpp | 27 +-- src/client/component/gsc/script_error.cpp | 216 +++++++++--------- src/client/component/gsc/script_error.hpp | 7 + src/client/component/gsc/script_extension.cpp | 165 ++++++++++--- src/client/component/gsc/script_extension.hpp | 27 +++ src/client/component/gsc/script_loading.cpp | 126 ++++++++-- src/client/component/logger.cpp | 2 +- src/client/component/map_rotation.cpp | 6 +- src/client/component/network.cpp | 2 +- src/client/component/notifies.cpp | 24 +- src/client/component/party.cpp | 112 +++++---- src/client/component/patches.cpp | 20 +- src/client/component/ranked.cpp | 2 +- src/client/component/rcon.cpp | 3 +- src/client/component/renderer.cpp | 2 +- src/client/component/scripting.cpp | 12 +- src/client/component/slowmotion.cpp | 29 +++ src/client/component/stats.cpp | 6 +- src/client/component/virtuallobby.cpp | 4 +- src/client/game/dvars.cpp | 42 ++-- src/client/game/dvars.hpp | 3 + src/client/game/structs.hpp | 16 +- src/client/game/symbols.hpp | 47 ++-- src/common/utils/properties.cpp | 25 ++ src/common/utils/properties.hpp | 7 + 47 files changed, 706 insertions(+), 372 deletions(-) create mode 100644 src/common/utils/properties.cpp create mode 100644 src/common/utils/properties.hpp diff --git a/deps/GSL b/deps/GSL index e64c97f..b8ac820 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d +Subproject commit b8ac820fe1efde9c63ca290f4ae8670f938a7dae diff --git a/deps/WinToast b/deps/WinToast index 821c481..a78ce46 160000 --- a/deps/WinToast +++ b/deps/WinToast @@ -1 +1 @@ -Subproject commit 821c4818ade1aa4da56ac753285c159ce26fd597 +Subproject commit a78ce469b456c06103b3b30d4bd37e7bb80da30c diff --git a/deps/asmjit b/deps/asmjit index 118ae6c..cfc9f81 160000 --- a/deps/asmjit +++ b/deps/asmjit @@ -1 +1 @@ -Subproject commit 118ae6ced160f68dd142a0d758aa7efb4356196a +Subproject commit cfc9f813cc6ccda63cad872edb32b38e0662bedb diff --git a/deps/gsc-tool b/deps/gsc-tool index b8e30e6..9ff3b87 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit b8e30e6334aa33ea731caf8d0700a9e6c7794c09 +Subproject commit 9ff3b871ef59b14eb8b32d258bf18347e14a3e18 diff --git a/deps/libtomcrypt b/deps/libtomcrypt index 7e863d2..c900951 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit 7e863d21429f94ed6a720e24499a12a3f852bb31 +Subproject commit c900951dab1bb94bab803fc57688dac18e3b71f9 diff --git a/deps/libtommath b/deps/libtommath index 8314bde..5809141 160000 --- a/deps/libtommath +++ b/deps/libtommath @@ -1 +1 @@ -Subproject commit 8314bde5e5c8e5d9331460130a9d1066e324f091 +Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016 diff --git a/deps/minhook b/deps/minhook index f5485b8..c1a7c38 160000 --- a/deps/minhook +++ b/deps/minhook @@ -1 +1 @@ -Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636 +Subproject commit c1a7c3843bd1a5fe3eb779b64c0d823bca3dc339 diff --git a/deps/rapidjson b/deps/rapidjson index 6089180..d621dc9 160000 --- a/deps/rapidjson +++ b/deps/rapidjson @@ -1 +1 @@ -Subproject commit 6089180ecb704cb2b136777798fa1be303618975 +Subproject commit d621dc9e9c77f81e5c8a35b8dcc16dcd63351321 diff --git a/deps/zlib b/deps/zlib index 643e17b..ef24c4c 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit 643e17b7498d12ab8d15565662880579692f769d +Subproject commit ef24c4c7502169f016dcd2a26923dbaf3216748c diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index 0051f69..a3b9781 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -5,14 +5,16 @@ #include "auth.hpp" #include "command.hpp" -#include "network.hpp" #include "console.hpp" +#include "network.hpp" -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include namespace auth { @@ -31,13 +33,26 @@ namespace auth std::string get_hw_profile_guid() { + auto hw_profile_path = (utils::properties::get_appdata_path() / "s1-guid.dat").generic_string(); + if (utils::io::file_exists(hw_profile_path)) + { + std::string hw_profile_info; + if (utils::io::read_file(hw_profile_path, &hw_profile_info) && !hw_profile_info.empty()) + { + return hw_profile_info; + } + } + HW_PROFILE_INFO info; if (!GetCurrentHwProfileA(&info)) { return {}; } - return std::string{info.szHwProfileGuid, sizeof(info.szHwProfileGuid)}; + auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; + utils::io::write_file(hw_profile_path, hw_profile_info); + + return hw_profile_info; } std::string get_protected_data() diff --git a/src/client/component/bullet.cpp b/src/client/component/bullet.cpp index 6b25a3e..a60b6ec 100644 --- a/src/client/component/bullet.cpp +++ b/src/client/component/bullet.cpp @@ -33,9 +33,7 @@ namespace bullet return; } - dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, - 0.0f, std::numeric_limits::max(), game::DVAR_FLAG_SAVED, - "Set to a value greater than 0 to override the surface penetration depth"); + dvars::bg_surfacePenetration = game::Dvar_RegisterFloat("bg_surfacePenetration", 0.0f, 0.0f, std::numeric_limits::max(), game::DVAR_FLAG_SAVED); bg_get_surface_penetration_depth_hook.create(0x1401641A0, &bg_get_surface_penetration_depth_stub); } }; diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 6408a9a..d41cd1e 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -253,7 +253,10 @@ namespace command void execute(std::string command, const bool sync) { - command += "\n"; + if (!command.ends_with('\n')) + { + command += "\n"; + } if (sync) { diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index 072f3ce..beef80d 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -177,9 +177,9 @@ namespace dedicated return; } - game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ, "Dedicated server"); + game::Dvar_RegisterBool("dedicated", true, game::DVAR_FLAG_READ); - sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE, "Don't send heartbeat"); + sv_lanOnly = game::Dvar_RegisterBool("sv_lanOnly", false, game::DVAR_FLAG_NONE); // Disable VirtualLobby dvars::override::register_bool("virtualLobbyEnabled", false, game::DVAR_FLAG_NONE | game::DVAR_FLAG_READ); diff --git a/src/client/component/dedicated_info.cpp b/src/client/component/dedicated_info.cpp index ef4b34a..ccdfcf9 100644 --- a/src/client/component/dedicated_info.cpp +++ b/src/client/component/dedicated_info.cpp @@ -19,28 +19,28 @@ namespace dedicated_info scheduler::loop([]() { - auto* sv_running = game::Dvar_FindVar("sv_running"); + const auto* sv_running = game::Dvar_FindVar("sv_running"); if (!sv_running || !sv_running->current.enabled) { console::set_title("s1-mod Dedicated Server"); return; } - auto* const sv_hostname = game::Dvar_FindVar("sv_hostname"); - auto* const sv_maxclients = game::Dvar_FindVar("sv_maxclients"); - auto* const mapname = game::Dvar_FindVar("mapname"); + const auto* sv_hostname = game::Dvar_FindVar("sv_hostname"); + const auto* sv_maxclients = game::Dvar_FindVar("sv_maxclients"); + const auto* mapname = game::Dvar_FindVar("mapname"); auto bot_count = 0; auto client_count = 0; - for (auto i = 0; i < sv_maxclients->current.integer; i++) + for (auto i = 0; i < sv_maxclients->current.integer; ++i) { auto* client = &game::mp::svs_clients[i]; auto* self = &game::mp::g_entities[i]; if (client->header.state >= 1 && self && self->client) { - client_count++; + ++client_count; if (game::SV_BotIsBot(i)) { ++bot_count; diff --git a/src/client/component/demonware.cpp b/src/client/component/demonware.cpp index c2c5156..a07cbc6 100644 --- a/src/client/component/demonware.cpp +++ b/src/client/component/demonware.cpp @@ -342,7 +342,7 @@ namespace demonware void bd_logger_stub(const char* const function, const char* const msg, ...) { - static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE, "Enable bdLogger"); + static const auto* bd_logger_enabled = game::Dvar_RegisterBool("bd_logger_enabled", false, game::DVAR_FLAG_NONE); if (!bd_logger_enabled->current.enabled) { return; diff --git a/src/client/component/dvar_cheats.cpp b/src/client/component/dvar_cheats.cpp index e2284f6..b105be0 100644 --- a/src/client/component/dvar_cheats.cpp +++ b/src/client/component/dvar_cheats.cpp @@ -181,7 +181,7 @@ namespace dvar_cheats constexpr auto value = false; #endif - dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED, "Allow cheat commands and dvars on this server"); + dvars::sv_cheats = game::Dvar_RegisterBool("sv_cheats", value, game::DVAR_FLAG_REPLICATED); } }; } diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index 2dd26b4..3490f8c 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -209,7 +209,7 @@ namespace dvars utils::hook::detour dvar_set_int_hook; utils::hook::detour dvar_set_string_hook; - game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags, const char* description) + game::dvar_t* dvar_register_bool_stub(const char* name, bool value, unsigned int flags) { auto* var = find_dvar(override::register_bool_overrides, name); if (var) @@ -218,11 +218,10 @@ namespace dvars flags = var->flags; } - return dvar_register_bool_hook.invoke(name, value, flags, description); + return dvar_register_bool_hook.invoke(name, value, flags); } - game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags, - const char* description) + game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags) { auto* var = find_dvar(override::register_float_overrides, name); if (var) @@ -233,11 +232,10 @@ namespace dvars flags = var->flags; } - return dvar_register_float_hook.invoke(name, value, min, max, flags, description); + return dvar_register_float_hook.invoke(name, value, min, max, flags); } - game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags, - const char* description) + game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags) { auto* var = find_dvar(override::register_int_overrides, name); if (var) @@ -248,10 +246,10 @@ namespace dvars flags = var->flags; } - return dvar_register_int_hook.invoke(name, value, min, max, flags, description); + return dvar_register_int_hook.invoke(name, value, min, max, flags); } - game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags, const char* description) + game::dvar_t* dvar_register_string_stub(const char* name, const char* value, unsigned int flags) { auto* var = find_dvar(override::register_string_overrides, name); if (var) @@ -260,11 +258,10 @@ namespace dvars flags = var->flags; } - return dvar_register_string_hook.invoke(name, value, flags, description); + return dvar_register_string_hook.invoke(name, value, flags); } - game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max, - unsigned int flags, const char* description) + game::dvar_t* dvar_register_vector2_stub(const char* name, float x, float y, float min, float max, unsigned int flags) { auto* var = find_dvar(override::register_vector2_overrides, name); if (var) @@ -276,11 +273,10 @@ namespace dvars flags = var->flags; } - return dvar_register_vector2_hook.invoke(name, x, y, min, max, flags, description); + return dvar_register_vector2_hook.invoke(name, x, y, min, max, flags); } - game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min, - float max, unsigned int flags, const char* description) + game::dvar_t* dvar_register_vector3_stub(const char* name, float x, float y, float z, float min, float max, unsigned int flags) { auto* var = find_dvar(override::register_vector3_overrides, name); if (var) @@ -293,7 +289,7 @@ namespace dvars flags = var->flags; } - return dvar_register_vector3_hook.invoke(name, x, y, z, min, max, flags, description); + return dvar_register_vector3_hook.invoke(name, x, y, z, min, max, flags); } void dvar_set_bool_stub(game::dvar_t* dvar, bool boolean) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 7bb1262..9d02af4 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -135,7 +135,7 @@ namespace fastfiles db_try_load_x_file_internal_hook.create(SELECT_VALUE(0x1401816F0, 0x1402741C0), &db_try_load_x_file_internal); db_find_x_asset_header_hook.create(game::DB_FindXAssetHeader, db_find_x_asset_header_stub); - dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts to binary format"); + dvars::g_dump_scripts = game::Dvar_RegisterBool("g_dumpScripts", false, game::DVAR_FLAG_NONE); command::add("loadzone", [](const command::params& params) { diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index 367f6c0..9196af1 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -67,6 +67,11 @@ namespace filesystem register_path("s1"); register_path(get_binary_directory() + "\\data"); + if (get_binary_directory() != std::filesystem::current_path()) + { + register_path(std::filesystem::current_path() / "data"); + } + // game's search paths register_path("devraw"); register_path("devraw_shared"); diff --git a/src/client/component/fps.cpp b/src/client/component/fps.cpp index 8edd2ed..a36dbb6 100644 --- a/src/client/component/fps.cpp +++ b/src/client/component/fps.cpp @@ -137,9 +137,9 @@ namespace fps } } - const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/, const char* description) + const game::dvar_t* cg_draw_fps_register_stub(const char* dvar_name, const char** value_list, const int default_index, unsigned int /*flags*/) { - cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED, description); + cg_drawFPS = game::Dvar_RegisterEnum(dvar_name, value_list, default_index, game::DVAR_FLAG_SAVED); return cg_drawFPS; } } @@ -173,12 +173,12 @@ namespace fps scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer); if (game::environment::is_mp()) { - cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Draw ping"); + cg_drawPing = game::Dvar_RegisterInt("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED); scheduler::loop(cg_draw_ping, scheduler::pipeline::renderer); } - game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED, "Show server latency"); - game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED, "Show FPS counter"); + game::Dvar_RegisterBool("cg_infobar_ping", false, game::DVAR_FLAG_SAVED); + game::Dvar_RegisterBool("cg_infobar_fps", false, game::DVAR_FLAG_SAVED); } }; } diff --git a/src/client/component/game_console.cpp b/src/client/component/game_console.cpp index 61c2bde..90e0167 100644 --- a/src/client/component/game_console.cpp +++ b/src/client/component/game_console.cpp @@ -746,33 +746,30 @@ namespace game_console // add our dvars dvars::con_inputBoxColor = game::Dvar_RegisterVec4("con_inputBoxColor", 0.2f, 0.2f, 0.2f, 0.9f, 0.0f, 1.0f, - game::DVAR_FLAG_SAVED, - "color of console input box"); + game::DVAR_FLAG_SAVED); dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4("con_inputHintBoxColor", 0.3f, 0.3f, 0.3f, 1.0f, 0.0f, 1.0f, - game::DVAR_FLAG_SAVED, "color of console input hint box"); + game::DVAR_FLAG_SAVED); dvars::con_outputBarColor = game::Dvar_RegisterVec4("con_outputBarColor", 0.5f, 0.5f, 0.5f, 0.6f, 0.0f, - 1.0f, game::DVAR_FLAG_SAVED, - "color of console output bar"); + 1.0f, game::DVAR_FLAG_SAVED); dvars::con_outputSliderColor = game::Dvar_RegisterVec4("con_outputSliderColor", 1.0f, 0.8f, 0.0f, 1.0f, 0.0f, 1.0f, - game::DVAR_FLAG_SAVED, "color of console output slider"); + game::DVAR_FLAG_SAVED); dvars::con_outputWindowColor = game::Dvar_RegisterVec4("con_outputWindowColor", 0.25f, 0.25f, 0.25f, 0.85f, 0.0f, - 1.0f, game::DVAR_FLAG_SAVED, "color of console output window"); + 1.0f, game::DVAR_FLAG_SAVED); dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4("con_inputDvarMatchColor", 1.0f, 1.0f, 0.8f, 1.0f, 0.0f, - 1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar"); + 1.0f, game::DVAR_FLAG_SAVED); dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4("con_inputDvarValueColor", 1.0f, 1.0f, 0.8f, 1.0f, 0.0f, - 1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar value"); + 1.0f, game::DVAR_FLAG_SAVED); dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4( "con_inputDvarInactiveValueColor", 0.8f, 0.8f, - 0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED, - "color of console inactive dvar value"); + 0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED); dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4("con_inputCmdMatchColor", 0.80f, 0.80f, 1.0f, 1.0f, 0.0f, - 1.0f, game::DVAR_FLAG_SAVED, "color of console matched command"); + 1.0f, game::DVAR_FLAG_SAVED); } }; } diff --git a/src/client/component/game_log.cpp b/src/client/component/game_log.cpp index b205183..23775aa 100644 --- a/src/client/component/game_log.cpp +++ b/src/client/component/game_log.cpp @@ -81,7 +81,7 @@ namespace game_log scheduler::once([] { - dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE, "Log file name"); + dvars::g_log = game::Dvar_RegisterString("g_log", "logs/games_mp.log", game::DVAR_FLAG_NONE); }, scheduler::pipeline::main); scripting::on_init([] @@ -102,9 +102,9 @@ namespace game_log g_log_printf("InitGame\n"); }); - scripting::on_shutdown([](int free_scripts) + scripting::on_shutdown([](const int clear_scripts) -> void { - console::info("==== ShutdownGame (%d) ====\n", free_scripts); + console::info("==== ShutdownGame (%d) ====\n", clear_scripts); g_log_printf("ShutdownGame:\n"); g_log_printf("------------------------------------------------------------\n"); diff --git a/src/client/component/gameplay.cpp b/src/client/component/gameplay.cpp index 1ac5649..633bba9 100644 --- a/src/client/component/gameplay.cpp +++ b/src/client/component/gameplay.cpp @@ -144,54 +144,45 @@ namespace gameplay public: void post_unpack() override { - dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, - game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo"); + dvars::player_sustainAmmo = game::Dvar_RegisterBool("player_sustainAmmo", false, game::DVAR_FLAG_REPLICATED); pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x1403DD050, 0x140162B20), &pm_weapon_use_ammo_stub); if (game::environment::is_sp()) return; // Implement player ejection dvar - dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, - "Flag whether player ejection is on or off"); + dvars::g_playerEjection = game::Dvar_RegisterBool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED); utils::hook::call(0x1402D5E4A, stuck_in_client_stub); // Implement player collision dvar - dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED, - "Flag whether player collision is on or off"); + dvars::g_playerCollision = game::Dvar_RegisterBool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED); utils::hook::call(0x1404563DA, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity utils::hook::call(0x1401F7F8F, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity // Implement bouncing dvar utils::hook::jump(0x14014DF91, pm_bouncing_stub_mp, true); - dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, - game::DVAR_FLAG_REPLICATED, "Enable bouncing"); + dvars::pm_bouncing = game::Dvar_RegisterBool("pm_bouncing", false, game::DVAR_FLAG_REPLICATED); // Change jump_slowdownEnable dvar flags to just "replicated" utils::hook::set(0x140135992, game::DVAR_FLAG_REPLICATED); // Choosing the following min/max because the game would truncate larger values - dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits::min(), - std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED, "Gravity in inches per second per second"); + dvars::g_gravity = game::Dvar_RegisterInt("g_gravity", 800, std::numeric_limits::min(), std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED); utils::hook::jump(0x1402D5A5D, client_end_frame_stub, true); utils::hook::nop(0x1402D5A69, 1); // Nop skipped opcode // Choosing the following min/max because the game would truncate larger values - dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190, - std::numeric_limits::min(), std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED, "Player speed"); + dvars::g_speed = game::Dvar_RegisterInt("g_speed", 190, std::numeric_limits::min(), std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED); utils::hook::jump(0x1402D6A8C, client_think_real_stub, true); utils::hook::nop(0x1402D6A98, 4); // Nop skipped opcodes - dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f, - 0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED, "The maximum height of a player's jump"); + dvars::jump_height = game::Dvar_RegisterFloat("jump_height", 39.0f, 0.0f, 1000.0f, game::DVAR_FLAG_REPLICATED); utils::hook::call(0x1401352FF, jump_start_stub); - dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f, - 0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED, "The velocity of a jump off of a ladder"); + dvars::jump_ladderPushVel = game::Dvar_RegisterFloat("jump_ladderPushVel", 128.0f, 0.0f, 1024.0f, game::DVAR_FLAG_REPLICATED); utils::hook::jump(0x1401358B3, jump_push_off_ladder, true); utils::hook::nop(0x1401358BF, 4); // Nop skipped opcodes - dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false, - game::DVAR_FLAG_REPLICATED, "Enable elevators"); + dvars::g_elevators = game::Dvar_RegisterBool("g_elevators", false, game::DVAR_FLAG_REPLICATED); utils::hook::call(0x140146134, pm_player_trace_stub); utils::hook::call(0x14014619B, pm_player_trace_stub); diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 867c003..a7c4106 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -186,100 +186,6 @@ namespace gsc } } } - - void scr_get_vector(unsigned int index, float* vector_value) - { - if (index < game::scr_VmPub->outparamcount) - { - auto* value = game::scr_VmPub->top - index; - if (value->type == game::VAR_VECTOR) - { - std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3])); - return; - } - - scr_error(va("Type %s is not a vector", var_typename[value->type])); - } - - scr_error(va("Parameter %u does not exist", index + 1)); - } - - int scr_get_int(unsigned int index) - { - if (index < game::scr_VmPub->outparamcount) - { - auto* value = game::scr_VmPub->top - index; - if (value->type == game::VAR_INTEGER) - { - return value->u.intValue; - } - - scr_error(va("Type %s is not an int", var_typename[value->type])); - } - - scr_error(va("Parameter %u does not exist", index + 1)); - return 0; - } - - float scr_get_float(unsigned int index) - { - if (index < game::scr_VmPub->outparamcount) - { - auto* value = game::scr_VmPub->top - index; - if (value->type == game::VAR_FLOAT) - { - return value->u.floatValue; - } - - if (value->type == game::VAR_INTEGER) - { - return static_cast(value->u.intValue); - } - - scr_error(va("Type %s is not a float", var_typename[value->type])); - } - - scr_error(va("Parameter %u does not exist", index + 1)); - return 0.0f; - } - - int scr_get_pointer_type(unsigned int index) - { - if (index < game::scr_VmPub->outparamcount) - { - if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER) - { - return static_cast(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue)); - } - - scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type])); - } - - scr_error(va("Parameter %u does not exist", index + 1)); - return 0; - } - - int scr_get_type(unsigned int index) - { - if (index < game::scr_VmPub->outparamcount) - { - return (game::scr_VmPub->top - index)->type; - } - - scr_error(va("Parameter %u does not exist", index + 1)); - return 0; - } - - const char* scr_get_type_name(unsigned int index) - { - if (index < game::scr_VmPub->outparamcount) - { - return var_typename[(game::scr_VmPub->top - index)->type]; - } - - scr_error(va("Parameter %u does not exist", index + 1)); - return nullptr; - } } std::optional> find_function(const char* pos) @@ -299,34 +205,130 @@ namespace gsc return {}; } + void scr_get_vector(unsigned int index, float* vector_value) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_VECTOR) + { + std::memcpy(vector_value, value->u.vectorValue, sizeof(std::float_t[3])); + return; + } + + scr_error(va("Type %s is not a vector", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + } + + int scr_get_int(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_INTEGER) + { + return value->u.intValue; + } + + scr_error(va("Type %s is not an int", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + float scr_get_float(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + auto* value = game::scr_VmPub->top - index; + if (value->type == game::VAR_FLOAT) + { + return value->u.floatValue; + } + + if (value->type == game::VAR_INTEGER) + { + return static_cast(value->u.intValue); + } + + scr_error(va("Type %s is not a float", var_typename[value->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0.0f; + } + + int scr_get_pointer_type(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + if ((game::scr_VmPub->top - index)->type == game::VAR_POINTER) + { + return static_cast(game::GetObjectType((game::scr_VmPub->top - index)->u.uintValue)); + } + + scr_error(va("Type %s is not an object", var_typename[(game::scr_VmPub->top - index)->type])); + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + int scr_get_type(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + return (game::scr_VmPub->top - index)->type; + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return 0; + } + + const char* scr_get_type_name(unsigned int index) + { + if (index < game::scr_VmPub->outparamcount) + { + return var_typename[(game::scr_VmPub->top - index)->type]; + } + + scr_error(va("Parameter %u does not exist", index + 1)); + return nullptr; + } + class error final : public component_interface { public: void post_unpack() override { + scr_emit_function_hook.create(SELECT_VALUE(0x1403113D0, 0x1403ED900), &scr_emit_function_stub); + + utils::hook::call(SELECT_VALUE(0x140311364, 0x1403ED894), compile_error_stub); // LinkFile + utils::hook::call(SELECT_VALUE(0x1403113B8, 0x1403ED8E8), compile_error_stub); // LinkFile + utils::hook::call(SELECT_VALUE(0x1403114AA, 0x1403ED9DB), find_variable_stub); // Scr_EmitFunction + + // Restore basic error messages for commonly used scr functions + utils::hook::jump(SELECT_VALUE(0x14031BD80, 0x1403F8510), scr_get_const_string); + utils::hook::jump(game::Scr_GetInt, scr_get_int); + utils::hook::jump(game::Scr_GetFloat, scr_get_float); + utils::hook::jump(game::Scr_GetVector, scr_get_vector); + + utils::hook::jump(SELECT_VALUE(0x14031C690, 0x1403F8D70), scr_get_type); + utils::hook::jump(SELECT_VALUE(0x14031C700, 0x1403F8DE0), scr_get_type_name); + if (game::environment::is_sp()) { return; } - scr_emit_function_hook.create(0x1403ED900, &scr_emit_function_stub); - - utils::hook::call(0x1403ED894, compile_error_stub); // LinkFile - utils::hook::call(0x1403ED8E8, compile_error_stub); // LinkFile - utils::hook::call(0x1403ED9DB, find_variable_stub); // Scr_EmitFunction - - // Restore basic error messages for commonly used scr functions utils::hook::jump(0x1403F8990, scr_get_object); - utils::hook::jump(0x1403F8510, scr_get_const_string); utils::hook::jump(0x1403F82F0, scr_get_const_istring); utils::hook::jump(0x140327870, scr_validate_localized_string_ref); - utils::hook::jump(0x1403F8EC0, scr_get_vector); - utils::hook::jump(0x1403F88D0, scr_get_int); - utils::hook::jump(0x1403F8820, scr_get_float); utils::hook::jump(0x1403F8BA0, scr_get_pointer_type); - utils::hook::jump(0x1403F8D70, scr_get_type); - utils::hook::jump(0x1403F8DE0, scr_get_type_name); } void pre_destroy() override diff --git a/src/client/component/gsc/script_error.hpp b/src/client/component/gsc/script_error.hpp index 284af43..94d77c8 100644 --- a/src/client/component/gsc/script_error.hpp +++ b/src/client/component/gsc/script_error.hpp @@ -3,4 +3,11 @@ namespace gsc { std::optional> find_function(const char* pos); + + void scr_get_vector(unsigned int index, float* vector_value); + int scr_get_int(unsigned int index); + float scr_get_float(unsigned int index); + int scr_get_pointer_type(unsigned int index); + int scr_get_type(unsigned int index); + const char* scr_get_type_name(unsigned int index); } diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index c45f4fb..d3cc974 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -5,15 +5,15 @@ #include "game/scripting/functions.hpp" -#include - -#include "component/console.hpp" #include "component/command.hpp" +#include "component/console.hpp" +#include "component/notifies.hpp" #include "script_error.hpp" #include "script_extension.hpp" #include "script_loading.hpp" +#include #include namespace gsc @@ -39,9 +39,60 @@ namespace gsc utils::hook::detour scr_register_function_hook; + std::vector devmap_entries{}; + + std::optional get_devmap_entry(const std::uint8_t* codepos) + { + const auto itr = std::ranges::find_if(devmap_entries, [codepos](const devmap_entry& entry) -> bool + { + return codepos >= entry.bytecode && codepos < entry.bytecode + entry.size; + }); + + if (itr != devmap_entries.end()) + { + return *itr; + } + + return {}; + } + + std::optional> get_line_and_col_for_codepos(const std::uint8_t* codepos) + { + const auto entry = get_devmap_entry(codepos); + + if (!entry.has_value()) + { + return {}; + } + + std::optional> best_line_info{}; + std::uint32_t best_codepos = 0; + + assert(codepos >= entry->bytecode); + const std::uint32_t codepos_offset = static_cast(codepos - entry->bytecode); + + for (const auto& instruction : entry->devmap) + { + if (instruction.codepos > codepos_offset) + { + continue; + } + + if (best_line_info.has_value() && codepos_offset - instruction.codepos > codepos_offset - best_codepos) + { + continue; + } + + best_line_info = { { instruction.line, instruction.col } }; + best_codepos = instruction.codepos; + } + + return best_line_info; + } + unsigned int scr_get_function_stub(const char** p_name, int* type) { - const auto result = utils::hook::invoke(0x1403318B0, p_name, type); + const auto result = game::Scr_GetFunction(p_name, type); for (const auto& [name, func] : functions) { @@ -122,27 +173,36 @@ namespace gsc const auto pos = frame == game::scr_VmPub->function_frame ? game::scr_function_stack->pos : frame->fs.pos; const auto function = find_function(frame->fs.pos); + const char* location; if (function.has_value()) { - console::warn("\tat function \"%s\" in file \"%s.gsc\"\n", function.value().first.data(), function.value().second.data()); + location = utils::string::va("function \"%s\" in file \"%s\"", function.value().first.data(), function.value().second.data()); } else { - console::warn("\tat unknown location %p\n", pos); + location = utils::string::va("unknown location %p", pos); } + + const auto line_info = get_line_and_col_for_codepos(reinterpret_cast(pos)); + if (line_info.has_value()) + { + location = utils::string::va("%s line \"%d\" column \"%d\"", location, line_info->first, line_info->second); + } + + console::warn("\tat %s\n", location); } } - void vm_error_stub(int mark_pos) + void vm_error_stub(unsigned __int64 mark_pos) { if (!dvars::com_developer_script->current.enabled && !force_error_print) { - utils::hook::invoke(0x1404B6790, mark_pos); + game::LargeLocalResetToMark(mark_pos); return; } console::warn("******* script runtime error ********\n"); - const auto opcode_id = *reinterpret_cast(0x148806768); + const auto opcode_id = *reinterpret_cast(SELECT_VALUE(0x14A19DE68, 0x148806768)); const std::string error = gsc_error_msg.has_value() ? std::format(": {}", gsc_error_msg.value()) : std::string(); @@ -168,7 +228,7 @@ namespace gsc print_callstack(); console::warn("************************************\n"); - utils::hook::invoke(0x1404B6790, mark_pos); + game::LargeLocalResetToMark(mark_pos); } void scr_register_function_stub(void* func, int type, unsigned int name) @@ -219,6 +279,46 @@ namespace gsc { scr_error(utils::string::va("Assert fail: %s", game::Scr_GetString(0))); } + + void scr_cmd_is_dedicated_server() + { + game::Scr_AddInt(game::environment::is_dedi()); + } + + const char* get_code_pos(const int index) + { + if (static_cast(index) >= game::scr_VmPub->outparamcount) + { + scr_error("Scr_GetCodePos: index is out of range"); + return ""; + } + + const auto* value = &game::scr_VmPub->top[-index]; + + if (value->type != game::VAR_FUNCTION) + { + scr_error("Scr_GetCodePos requires a function as parameter"); + return ""; + } + + return value->u.codePosValue; + } + } + + void add_devmap_entry(std::uint8_t* codepos, std::size_t size, const std::string& name, xsk::gsc::buffer devmap_buf) + { + std::vector devmap{}; + const auto* devmap_ptr = reinterpret_cast(devmap_buf.data); + + devmap.resize(devmap_ptr->num_instructions); + std::memcpy(devmap.data(), devmap_ptr->instructions, sizeof(dev_map_instruction) * devmap_ptr->num_instructions); + + devmap_entries.emplace_back(codepos, size, name, std::move(devmap)); + } + + void clear_devmap() + { + devmap_entries.clear(); } void scr_error(const char* error) @@ -252,33 +352,40 @@ namespace gsc override_function("print", &scr_print); override_function("println", &scr_print_ln); - utils::hook::set(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction - - utils::hook::set(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction - utils::hook::set(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0 - utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts - - if (game::environment::is_sp()) - { - return; - } - - utils::hook::nop(0x1403FB7F7 + 5, 2); - utils::hook::call(0x1403FB7F7, vm_call_builtin_function); - - utils::hook::call(0x1403FCAB0, vm_error_stub); // LargeLocalResetToMark - - utils::hook::call(0x1403EDF1F, scr_get_function_stub); - override_function("assert", &assert_cmd); override_function("assertex", &assert_ex_cmd); override_function("assertmsg", &assert_msg_cmd); - add_function("executecommand", [] + override_function("isdedicatedserver", &scr_cmd_is_dedicated_server); + + add_function("replacefunc", [] + { + if (scr_get_type(0) != game::VAR_FUNCTION || scr_get_type(1) != game::VAR_FUNCTION) + { + throw gsc_error("Parameter must be a function"); + } + + notifies::set_gsc_hook(get_code_pos(0), get_code_pos(1)); + }); + + add_function("executecommand", []() -> void { const auto* cmd = game::Scr_GetString(0); command::execute(cmd); }); + + utils::hook::set(SELECT_VALUE(0x1403115BC, 0x1403EDAEC), 0x1000); // Scr_RegisterFunction + + utils::hook::set(SELECT_VALUE(0x1403115C2 + 4, 0x1403EDAF2 + 4), RVA(&func_table)); // Scr_RegisterFunction + // utils::hook::set(SELECT_VALUE(0x14031F097 + 3, 0x1403FB7F7 + 3), RVA(&func_table)); // VM_Execute_0 + utils::hook::inject(SELECT_VALUE(0x140311964 + 3, 0x1403EDEE4 + 3), &func_table); // Scr_BeginLoadScripts + + utils::hook::nop(SELECT_VALUE(0x14031F097 + 5, 0x1403FB7F7 + 5), 2); + utils::hook::call(SELECT_VALUE(0x14031F097, 0x1403FB7F7), vm_call_builtin_function); + + utils::hook::call(SELECT_VALUE(0x14032034F, 0x1403FCAB0), vm_error_stub); // LargeLocalResetToMark + + utils::hook::call(SELECT_VALUE(0x14031199F, 0x1403EDF1F), scr_get_function_stub); } }; } diff --git a/src/client/component/gsc/script_extension.hpp b/src/client/component/gsc/script_extension.hpp index 9fab151..126260e 100644 --- a/src/client/component/gsc/script_extension.hpp +++ b/src/client/component/gsc/script_extension.hpp @@ -1,9 +1,36 @@ #pragma once +#include namespace gsc { extern void* func_table[0x1000]; +#pragma pack(push, 1) + struct dev_map_instruction + { + std::uint32_t codepos; + std::uint16_t line; + std::uint16_t col; + }; + + struct dev_map + { + std::uint32_t num_instructions; + dev_map_instruction instructions[1]; + }; +#pragma pack(pop) + + struct devmap_entry + { + const std::uint8_t* bytecode; + std::size_t size; + std::string script_name; + std::vector devmap; + }; + + void add_devmap_entry(std::uint8_t*, std::size_t, const std::string&, xsk::gsc::buffer); + void clear_devmap(); + void scr_error(const char* error); void override_function(const std::string& name, game::BuiltinFunction func); void add_function(const std::string& name, game::BuiltinFunction function); diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 1010774..83cfacc 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -12,6 +12,7 @@ #include "component/console.hpp" #include "component/scripting.hpp" +#include "script_extension.hpp" #include "script_loading.hpp" namespace gsc @@ -32,6 +33,7 @@ namespace gsc init_handles.clear(); loaded_scripts.clear(); script_allocator.clear(); + clear_devmap(); } bool read_raw_script_file(const std::string& name, std::string* data) @@ -115,6 +117,12 @@ namespace gsc loaded_scripts[real_name] = script_file_ptr; + const auto devmap = std::get<2>(output_script); + if (devmap.size > 0 && (gsc_ctx->build() & xsk::gsc::build::dev_maps) != xsk::gsc::build::prod) + { + add_devmap_entry(script_file_ptr->bytecode, byte_code_size, real_name, devmap); + } + return script_file_ptr; } catch (const std::exception& ex) @@ -181,13 +189,10 @@ namespace gsc } } - void load_scripts(const std::filesystem::path& root_dir) + + void load_scripts_from_folder(const std::filesystem::path& root_dir, const std::filesystem::path& script_dir) { - const std::filesystem::path script_dir = root_dir / "scripts"; - if (!utils::io::directory_exists(script_dir.generic_string())) - { - return; - } + console::info("Scanning directory '%s' for custom GSC scripts...\n", script_dir.generic_string().data()); const auto scripts = utils::io::list_files(script_dir.generic_string()); for (const auto& script : scripts) @@ -205,6 +210,44 @@ namespace gsc } } + void load_scripts(const std::filesystem::path& root_dir) + { + const auto load = [&root_dir](const std::filesystem::path& folder) -> void + { + const std::filesystem::path script_dir = root_dir / folder; + if (utils::io::directory_exists(script_dir.generic_string())) + { + load_scripts_from_folder(root_dir, script_dir); + } + }; + + const std::filesystem::path base_dir = "scripts"; + + load(base_dir); + + const auto* map_name = game::Dvar_FindVar("mapname"); + + if (game::environment::is_sp()) + { + const std::filesystem::path game_folder = "sp"; + + load(base_dir / game_folder); + + load(base_dir / game_folder / map_name->current.string); + } + else + { + const std::filesystem::path game_folder = "mp"; + + load(base_dir / game_folder); + + load(base_dir / game_folder / map_name->current.string); + + const auto* game_type = game::Dvar_FindVar("g_gametype"); + load(base_dir / game_folder / game_type->current.string); + } + } + int db_is_x_asset_default(game::XAssetType type, const char* name) { if (loaded_scripts.contains(name)) @@ -224,8 +267,6 @@ namespace gsc return; } - clear(); - for (const auto& path : filesystem::get_search_paths()) { load_scripts(path); @@ -259,7 +300,38 @@ namespace gsc utils::hook::invoke(0x1403380D0); } - void scr_load_level_stub() + int g_scr_set_level_script_stub(game::ScriptFunctions* functions) + { + const auto result = utils::hook::invoke(0x140262F60, functions); + + for (const auto& path : filesystem::get_search_paths()) + { + load_scripts(path); + } + + return result; + } + + void scr_load_level_singleplayer_stub() + { + for (auto& function_handle : main_handles) + { + console::info("Executing '%s::main'\n", function_handle.first.data()); + const auto thread = game::Scr_ExecThread(static_cast(function_handle.second), 0); + game::RemoveRefToObject(thread); + } + + utils::hook::invoke(0x140257720); + + for (auto& function_handle : init_handles) + { + console::info("Executing '%s::init'\n", function_handle.first.data()); + const auto thread = game::Scr_ExecThread(static_cast(function_handle.second), 0); + game::RemoveRefToObject(thread); + } + } + + void scr_load_level_multiplayer_stub() { utils::hook::invoke(0x140325B90); @@ -358,28 +430,32 @@ namespace gsc utils::hook::call(SELECT_VALUE(0x1403309E9, 0x1403309E9), scr_begin_load_scripts_stub); // GScr_LoadScripts utils::hook::call(SELECT_VALUE(0x14023DA84, 0x140330B9C), scr_end_load_scripts_stub); // GScr_LoadScripts - dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE, "Enable development options"); - dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments"); + // ProcessScript + utils::hook::call(SELECT_VALUE(0x14031AB47, 0x1403F7317), find_script); + utils::hook::call(SELECT_VALUE(0x14031AB57, 0x1403F7327), db_is_x_asset_default); + + dvars::com_developer = game::Dvar_RegisterInt("developer", 0, 0, 2, game::DVAR_FLAG_NONE); + dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE); if (game::environment::is_sp()) { - return; + utils::hook::call(0x1402632A5, g_scr_set_level_script_stub); + + utils::hook::call(0x140226931, scr_load_level_singleplayer_stub); + } + else + { + // GScr_LoadScripts + utils::hook::call(0x140330B97, gscr_post_load_scripts_stub); + + // Exec script handles + utils::hook::call(0x1402F71AE, g_load_structs_stub); + utils::hook::call(0x1402F71C7, scr_load_level_multiplayer_stub); } - // ProcessScript - utils::hook::call(0x1403F7317, find_script); - utils::hook::call(0x1403F7327, db_is_x_asset_default); - - // GScr_LoadScripts - utils::hook::call(0x140330B97, gscr_post_load_scripts_stub); - - // Exec script handles - utils::hook::call(0x1402F71AE, g_load_structs_stub); - utils::hook::call(0x1402F71C7, scr_load_level_stub); - - scripting::on_shutdown([](int free_scripts) + scripting::on_shutdown([](const int clear_scripts) -> void { - if (free_scripts) + if (clear_scripts) { clear(); } diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index 68306e8..60eb61d 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -164,7 +164,7 @@ namespace logger com_error_hook.create(game::Com_Error, com_error_stub); - logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED, "Print dev stuff"); + logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED); } }; } diff --git a/src/client/component/map_rotation.cpp b/src/client/component/map_rotation.cpp index 832985d..f5f748b 100644 --- a/src/client/component/map_rotation.cpp +++ b/src/client/component/map_rotation.cpp @@ -289,11 +289,11 @@ namespace map_rotation scheduler::once([] { - sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE, ""); - sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE, ""); + sv_map_rotation = game::Dvar_RegisterString("sv_mapRotation", "", game::DVAR_FLAG_NONE); + sv_map_rotation_current = game::Dvar_RegisterString("sv_mapRotationCurrent", "", game::DVAR_FLAG_NONE); }, scheduler::pipeline::main); - sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE, "Randomize map rotation"); + sv_random_map_rotation = game::Dvar_RegisterBool("sv_randomMapRotation", false, game::DVAR_FLAG_NONE); command::add("map_rotate", &perform_map_rotation); diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 3c36aa4..7d05af8 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -179,7 +179,7 @@ namespace network game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags, const char* description) { - auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED, "Network port"); + auto dvar = game::Dvar_RegisterInt("net_port", 27016, 0, 0xFFFFu, game::DVAR_FLAG_LATCHED); // read net_port from command line command::read_startup_variable("net_port"); diff --git a/src/client/component/notifies.cpp b/src/client/component/notifies.cpp index 6a449d9..80ba113 100644 --- a/src/client/component/notifies.cpp +++ b/src/client/component/notifies.cpp @@ -28,8 +28,6 @@ namespace notifies char empty_function[2] = {0x32, 0x34}; // CHECK_CLEAR_PARAMS, END const char* target_function = nullptr; - - void client_command_stub(const int client_num) { if (game::mp::g_entities[client_num].client == nullptr) @@ -137,7 +135,7 @@ namespace notifies a.lea(eax, dword_ptr(r15, -0x17)); a.mov(dword_ptr(rbp, 0x68), r15d); - a.jmp(0x1403FA143); + a.jmp(SELECT_VALUE(0x14031D9E3, 0x1403FA143)); a.bind(replace); @@ -185,22 +183,22 @@ namespace notifies public: void post_unpack() override { + utils::hook::jump(SELECT_VALUE(0x14031D9D4, 0x1403FA134), utils::hook::assemble(vm_execute_stub), true); + + scripting::on_shutdown([](const int clear_scripts) -> void + { + if (clear_scripts) + { + vm_execute_hooks.clear(); + } + }); + if (game::environment::is_sp()) { return; } utils::hook::call(0x14043A9AD, client_command_stub); - - utils::hook::jump(0x1403FA134, utils::hook::assemble(vm_execute_stub), true); - - scripting::on_shutdown([](bool free_scripts) - { - if (free_scripts) - { - vm_execute_hooks.clear(); - } - }); } }; } diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 5e06767..15905c8 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/dvars.hpp" #include "party.hpp" #include "console.hpp" @@ -33,6 +34,29 @@ namespace party int sv_maxclients; + utils::info_string get_info() + { + utils::info_string info; + + info.set("gamename", "S1"); + info.set("hostname", dvars::get_dvar_string("sv_hostname")); + info.set("gametype", dvars::get_dvar_string("g_gametype")); + info.set("sv_motd", dvars::get_dvar_string("sv_motd")); + info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits)); + info.set("mapname", dvars::get_dvar_string("mapname")); + info.set("isPrivate", dvars::get_dvar_string("g_password").empty() ? "0" : "1"); + info.set("clients", std::to_string(get_client_count())); + info.set("bots", std::to_string(get_bot_count())); + info.set("sv_maxclients", std::to_string(game::Dvar_FindVar("sv_maxclients")->current.integer)); + info.set("protocol", std::to_string(PROTOCOL)); + info.set("shortversion", SHORTVERSION); + info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode())); + info.set("sv_running", std::to_string(dvars::get_dvar_bool("sv_running"))); + info.set("dedicated", std::to_string(dvars::get_dvar_bool("dedicated"))); + + return info; + } + void perform_game_initialization() { command::execute("onlinegame 1", true); @@ -79,30 +103,7 @@ namespace party // CL_ConnectFromParty char session_info[0x100] = {}; - reinterpret_cast(0x140209360)( - 0, session_info, &target, mapname.data(), gametype.data()); - } - - std::string get_dvar_string(const std::string& dvar) - { - const auto* dvar_value = game::Dvar_FindVar(dvar.data()); - if (dvar_value && dvar_value->current.string) - { - return {dvar_value->current.string}; - } - - return {}; - } - - bool get_dvar_bool(const std::string& dvar) - { - const auto* dvar_value = game::Dvar_FindVar(dvar.data()); - if (dvar_value && dvar_value->current.enabled) - { - return dvar_value->current.enabled; - } - - return false; + reinterpret_cast(0x140209360)(0, session_info, &target, mapname.data(), gametype.data()); } void didyouknow_stub(const char* dvar_name, const char* string) @@ -461,8 +462,7 @@ namespace party scheduler::once([]() { - game::Dvar_RegisterString("sv_sayName", "console", game::DvarFlags::DVAR_FLAG_NONE, - "The name to pose as for 'say' commands"); + game::Dvar_RegisterString("sv_sayName", "console", game::DVAR_FLAG_NONE); }, scheduler::pipeline::main); command::add("tell", [](const command::params& params) @@ -529,27 +529,55 @@ namespace party network::on("getInfo", [](const game::netadr_s& target, const std::string& data) { - utils::info_string info{}; + utils::info_string info = get_info(); info.set("challenge", data); - info.set("gamename", "S1"); - info.set("hostname", get_dvar_string("sv_hostname")); - info.set("gametype", get_dvar_string("g_gametype")); - info.set("sv_motd", get_dvar_string("sv_motd")); - info.set("xuid", utils::string::va("%llX", steam::SteamUser()->GetSteamID().bits)); - info.set("mapname", get_dvar_string("mapname")); - info.set("isPrivate", get_dvar_string("g_password").empty() ? "0" : "1"); - info.set("clients", std::to_string(get_client_count())); - info.set("bots", std::to_string(get_bot_count())); - info.set("sv_maxclients", std::to_string(*game::mp::svs_numclients)); - info.set("protocol", std::to_string(PROTOCOL)); - info.set("playmode", utils::string::va("%i", game::Com_GetCurrentCoDPlayMode())); - info.set("sv_running", std::to_string(get_dvar_bool("sv_running"))); - info.set("dedicated", std::to_string(get_dvar_bool("dedicated"))); - info.set("shortversion", SHORTVERSION); network::send(target, "infoResponse", info.build(), '\n'); }); + network::on("getStatus", [](const game::netadr_s& target, const std::string& data) + { + std::string player_list; + + utils::info_string info = get_info(); + info.set("challenge", data); + + const auto* sv_running = game::Dvar_FindVar("sv_running"); + if (!sv_running || !sv_running->current.enabled) + { + return; + } + + for (auto i = 0; i < game::Dvar_FindVar("sv_maxclients")->current.integer; ++i) + { + auto* client = &game::mp::svs_clients[i]; + auto* self = &game::mp::g_entities[i]; + + if (client->header.state < 5) + { + continue; + } + + if (!self || !self->client) + { + continue; + } + + if (game::SV_BotIsBot(i)) + { + continue; + } + + const auto score = game::G_GetClientScore(i); + const auto ping = game::SV_GetClientPing(i); + const std::string name = game::mp::svs_clients[i].name; + + player_list.append(std::format("{} {} \"{}\"\n", score, ping, name)); + } + + network::send(target, "statusResponse", info.build() + "\n"s + player_list + "\n"s, '\n'); + }); + if (game::environment::is_dedi()) { return; diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index e810bcb..4cc58a1 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -58,28 +58,28 @@ namespace patches if (game::environment::is_mp()) { // Make name save - game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name."); + game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED); // Disable data validation error popup - game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, ""); + game::Dvar_RegisterInt("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE); } return com_register_dvars_hook.invoke(); } - game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/, const char* description) + game::dvar_t* register_com_maxfps_stub(const char* name, int /*value*/, int /*min*/, int /*max*/, const unsigned int /*flags*/) { - return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED, description); + return game::Dvar_RegisterInt(name, 0, 0, 1000, game::DVAR_FLAG_SAVED); } - game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/, const char* description) + game::dvar_t* register_cg_fov_stub(const char* name, float value, float min, float /*max*/, const unsigned int /*flags*/) { - return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED, description); + return game::Dvar_RegisterFloat(name, value, min, 160, game::DVAR_FLAG_SAVED); } - game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/, const char* desc) + game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, unsigned int /*flags*/) { - return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED, desc); + return game::Dvar_RegisterFloat(name, 1.0f, 0.2f, 5.0f, game::DVAR_FLAG_SAVED); } int dvar_command_patch() // game makes this return an int and compares with eax instead of al -_- @@ -252,7 +252,7 @@ namespace patches utils::hook::call(0x14026E63B, bsp_sys_error_stub); // client side aim assist dvar - dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED, "Enables aim assist for controllers"); + dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, game::DVAR_FLAG_SAVED); utils::hook::call(0x140003609, aim_assist_add_to_target_list); // isProfanity @@ -304,7 +304,7 @@ namespace patches dvars::override::register_int("cl_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // Seems unused dvars::override::register_int("sv_connectTimeout", 120, 120, 1800, game::DVAR_FLAG_NONE); // 60 - 0 - 1800 - game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED, ""); + game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED); // Prevent clients from ending the game as non host by sending 'end_game' lui notification cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub); diff --git a/src/client/component/ranked.cpp b/src/client/component/ranked.cpp index 238215b..6d6df76 100644 --- a/src/client/component/ranked.cpp +++ b/src/client/component/ranked.cpp @@ -31,7 +31,7 @@ namespace ranked dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE); // Some dvar used in gsc - game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE, "Force ranking"); + game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE); } // Always run bots, even if xblive_privatematch is 0 diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp index 2c8ea40..8cbb4fa 100644 --- a/src/client/component/rcon.cpp +++ b/src/client/component/rcon.cpp @@ -128,8 +128,7 @@ namespace rcon scheduler::once([]() { - game::Dvar_RegisterString("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE, - "The password for remote console"); + game::Dvar_RegisterString("rcon_password", "", game::DVAR_FLAG_NONE); }, scheduler::pipeline::main); command::add("status", []() diff --git a/src/client/component/renderer.cpp b/src/client/component/renderer.cpp index 709095a..9a0636a 100644 --- a/src/client/component/renderer.cpp +++ b/src/client/component/renderer.cpp @@ -62,7 +62,7 @@ namespace renderer return; } - dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); + dvars::r_fullbright = game::Dvar_RegisterInt("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED); r_init_draw_method_hook.create(SELECT_VALUE(0x14046C150, 0x140588B00), &r_init_draw_method_stub); r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404A5330, 0x1405C3AE0), &r_update_front_end_dvar_options_stub); diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index d4579c0..a44bbe6 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -49,9 +49,9 @@ namespace scripting } } - void g_shutdown_game_stub(const int free_scripts) + void g_shutdown_game_stub(const int clear_scripts) { - if (free_scripts) + if (clear_scripts) { script_function_table_sort.clear(); script_function_table.clear(); @@ -61,10 +61,10 @@ namespace scripting for (const auto& callback : shutdown_callbacks) { - callback(free_scripts); + callback(clear_scripts); } - return g_shutdown_game_hook.invoke(free_scripts); + return g_shutdown_game_hook.invoke(clear_scripts); } void process_script_stub(const char* filename) @@ -182,10 +182,6 @@ namespace scripting scr_set_thread_position_hook.create(SELECT_VALUE(0x1403115E0, 0x1403EDB10), &scr_set_thread_position_stub); process_script_hook.create(SELECT_VALUE(0x14031AB30, 0x1403F7300), &process_script_stub); sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, &sl_get_canonical_string_stub); - - scheduler::loop([] - { - }, scheduler::pipeline::server); } }; } diff --git a/src/client/component/slowmotion.cpp b/src/client/component/slowmotion.cpp index 6af2946..f0f4b77 100644 --- a/src/client/component/slowmotion.cpp +++ b/src/client/component/slowmotion.cpp @@ -2,6 +2,9 @@ #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "command.hpp" +#include "console.hpp" + #include #include @@ -33,6 +36,25 @@ namespace slowmotion game::SV_SetConfigstring(10, utils::string::va("%i %i %g %g", *game::mp::gameTime, duration, start, end)); game::Com_SetSlowMotion(start, end, duration); } + + void set_code_time_scale(float timescale) + { + assert(timescale > 0.0f); + *game::com_codeTimeScale = timescale; + } + + void com_timescale_f(const command::params& params) + { + if (params.size() != 2) + { + console::info("timescale \n"); + return; + } + + const auto timescale = static_cast(std::atof(params.get(1))); + console::info("timescale set to %f\n", timescale); + set_code_time_scale(timescale); + } } class component final : public component_interface @@ -40,6 +62,13 @@ namespace slowmotion public: void post_unpack() override { + if (game::environment::is_sp()) + { + return; + } + + command::add("timescale", com_timescale_f); + if (!game::environment::is_dedi()) { return; diff --git a/src/client/component/stats.cpp b/src/client/component/stats.cpp index 90d167c..83d9aac 100644 --- a/src/client/component/stats.cpp +++ b/src/client/component/stats.cpp @@ -75,9 +75,9 @@ namespace stats else { // unlock all - cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED, "Unlock items that are level-locked by the player's stats."); - cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED, "Unlock supply drop loot."); - game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED, "Unlock extra class slots."); + cg_unlock_all_items = game::Dvar_RegisterBool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED); + cg_unlock_all_loot = game::Dvar_RegisterBool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED); + game::Dvar_RegisterBool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED); is_item_locked_hook1.create(0x1403BD790, is_item_locked_stub1); // LiveStorage_IsItemUnlockedFromTable_LocalClient is_item_locked_hook2.create(0x1403BD290, is_item_locked_stub2); // LiveStorage_IsItemUnlockedFromTable diff --git a/src/client/component/virtuallobby.cpp b/src/client/component/virtuallobby.cpp index 1975dc4..00e6fd7 100644 --- a/src/client/component/virtuallobby.cpp +++ b/src/client/component/virtuallobby.cpp @@ -82,8 +82,8 @@ namespace virtuallobby return; } - virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED, "Field of view for the virtual lobby"); - virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby"); + virtualLobby_fov = game::Dvar_RegisterFloat("virtualLobby_fov", 40.0f, 1.0f, 170.0f, game::DVAR_FLAG_SAVED); + virtualLobby_fovscale = game::Dvar_RegisterFloat("virtualLobby_fovScale", 1.0f, 0.0f, 2.0f, game::DVAR_FLAG_SAVED); utils::hook::nop(0x1401D5AFB, 16); utils::hook::jump(0x1401D5AFB, get_fov_stub, true); diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 211a33d..6eb82d2 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -43,6 +43,28 @@ namespace dvars game::dvar_t* com_developer = nullptr; game::dvar_t* com_developer_script = nullptr; + std::string get_dvar_string(const std::string& dvar) + { + const auto* dvar_value = game::Dvar_FindVar(dvar.data()); + if (dvar_value && dvar_value->current.string) + { + return { dvar_value->current.string }; + } + + return {}; + } + + bool get_dvar_bool(const std::string& dvar) + { + const auto* dvar_value = game::Dvar_FindVar(dvar.data()); + if (dvar_value && dvar_value->current.enabled) + { + return dvar_value->current.enabled; + } + + return false; + } + std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain) { if (domain.vector.min == -FLT_MAX) @@ -51,22 +73,16 @@ namespace dvars { return utils::string::va("Domain is any %iD vector", components); } - else - { - return utils::string::va("Domain is any %iD vector with components %g or smaller", components, - domain.vector.max); - } + + return utils::string::va("Domain is any %iD vector with components %g or smaller", components, domain.vector.max); } - else if (domain.vector.max == FLT_MAX) + + if (domain.vector.max == FLT_MAX) { - return utils::string::va("Domain is any %iD vector with components %g or bigger", components, - domain.vector.min); - } - else - { - return utils::string::va("Domain is any %iD vector with components from %g to %g", components, - domain.vector.min, domain.vector.max); + return utils::string::va("Domain is any %iD vector with components %g or bigger", components, domain.vector.min); } + + return utils::string::va("Domain is any %iD vector with components from %g to %g", components, domain.vector.min, domain.vector.max); } std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain) diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index 40bbe82..2ba7ad9 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -42,6 +42,9 @@ namespace dvars extern game::dvar_t* com_developer; extern game::dvar_t* com_developer_script; + std::string get_dvar_string(const std::string& dvar); + bool get_dvar_bool(const std::string& dvar); + std::string dvar_get_vector_domain(int components, const game::dvar_limits& domain); std::string dvar_get_domain(game::dvar_type type, const game::dvar_limits& domain); } diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 5433582..dbef2e0 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -60,6 +60,13 @@ namespace game VAR_TOTAL_COUNT = 0x1B, }; + struct ScriptFunctions + { + int maxSize; + int count; + int* address; + }; + struct VariableStackBuffer { const char* pos; @@ -214,7 +221,6 @@ namespace game unsigned __int16 childVariableBucket[65536]; ChildVariableValue childVariableValue[384000]; }; - // * enum Sys_Folder { @@ -1483,11 +1489,17 @@ namespace game char __pad2[8]; int nextSnapshotTime; // 269024 char __pad3[544]; - LiveClientDropType liveDropRequest; //269572 + LiveClientDropType liveDropRequest; // 269572 char __pad4[24]; TestClientType testClient; // 269600 char __pad5[391700]; }; // size = 661304 + + static_assert(offsetof(client_t, gentity) == 0x41AB0); + static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0); + static_assert(offsetof(client_t, liveDropRequest) == 0x41D04); + static_assert(offsetof(client_t, testClient) == 0x41D20); + static_assert(sizeof(client_t) == 0xA1738); } namespace sp diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 98c9ae2..44e014a 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -12,7 +12,7 @@ namespace game WEAK symbol AddRefToObject{0, 0x1403F1F10}; WEAK symbol AllocThread{0, 0x1403F2270}; WEAK symbol RemoveRefToValue{0x140317340, 0x1403F3A50}; - WEAK symbol RemoveRefToObject{0, 0x1403F3940}; + WEAK symbol RemoveRefToObject{0x140317230, 0x1403F3940}; WEAK symbol AimAssist_AddToTargetList{0, 0x140001730}; @@ -70,22 +70,14 @@ namespace game WEAK symbol Dvar_SetFromStringByNameFromSource{0x1403737D0, 0x1404C2E40}; WEAK symbol Dvar_ValueToString{0x140374E10, 0x1404C47B0}; - WEAK symbol - Dvar_RegisterBool{0x140371850, 0x1404C0BE0}; - WEAK symbol Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0}; - WEAK symbol Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0}; - WEAK symbol - Dvar_RegisterInt{0x140371CF0, 0x1404C1080}; - WEAK symbol - Dvar_RegisterString{0x140372050, 0x1404C1450}; - WEAK symbol Dvar_RegisterVec2{0x140372120, 0x1404C1520}; - WEAK symbol Dvar_RegisterVec3{0x140372230, 0x1404C1600}; - WEAK symbol Dvar_RegisterVec4{0x140372430, 0x1404C1800}; + WEAK symbol Dvar_RegisterBool{0x140371850, 0x1404C0BE0}; + WEAK symbol Dvar_RegisterEnum{0x140371B30, 0x1404C0EC0}; + WEAK symbol Dvar_RegisterFloat{0x140371C20, 0x1404C0FB0}; + WEAK symbol Dvar_RegisterInt{0x140371CF0, 0x1404C1080}; + WEAK symbol Dvar_RegisterString{0x140372050, 0x1404C1450}; + WEAK symbol Dvar_RegisterVec2{0x140372120, 0x1404C1520}; + WEAK symbol Dvar_RegisterVec3{0x140372230, 0x1404C1600}; + WEAK symbol Dvar_RegisterVec4{0x140372430, 0x1404C1800}; WEAK symbol dwGetLogOnStatus{0, 0x14053CCB0}; @@ -96,7 +88,7 @@ namespace game WEAK symbol FS_AddLocalizedGameDirectory{0x14035F5C0, 0x1404AD170}; WEAK symbol GScr_LoadConsts{0x140283970, 0x1403479C0}; - WEAK symbol FindVariable{0x1403165D0, 0x1403F2DC0}; + WEAK symbol FindVariable{0x1403166D0, 0x1403F2DC0}; WEAK symbol FindEntityId{0x1403166D0, 0x1403F2CC0}; WEAK symbol GetVariableName{0x1403170E0, 0x1403F37F0}; WEAK symbol GetEntityFieldValue{0x14031AAD0, 0x1403F72A0}; @@ -152,21 +144,23 @@ namespace game WEAK symbol Scr_AddInt{0x0, 0x1403F7B30}; WEAK symbol Scr_GetInt{0x14031C1F0, 0x1403F88D0}; WEAK symbol Scr_GetFloat{0x14031C090, 0x1403F8820}; + WEAK symbol Scr_GetVector{0x14031C7E0, 0x1403F8EC0}; WEAK symbol Scr_GetNumParam{0x14031C2A0, 0x1403F8980}; WEAK symbol Scr_ClearOutParams{0x14031B7C0, 0x1403F8040}; WEAK symbol Scr_GetEntityIdRef{0x14031A0D0, 0x1403F68A0}; WEAK symbol Scr_SetObjectField{0x14026B620, 0x140339450}; WEAK symbol Scr_Notify{0x0, 0x140339120}; WEAK symbol Scr_NotifyId{0x14031CB80, 0x1403F92D0}; - WEAK symbol Scr_CastString{0x0, 0x1403F4500}; + WEAK symbol Scr_CastString{0x140317EA0, 0x1403F4500}; - WEAK symbol Scr_ExecThread{0x0, 0x1403F8120}; - WEAK symbol Scr_LoadScript{0x0, 0x1403EE250}; - WEAK symbol Scr_GetFunctionHandle{0x0, 0x1403EE0D0}; + WEAK symbol Scr_ExecThread{0x14031B8C0, 0x1403F8120}; + WEAK symbol Scr_LoadScript{0x140311CD0, 0x1403EE250}; + WEAK symbol Scr_GetFunctionHandle{0x140311B50, 0x1403EE0D0}; WEAK symbol Scr_RegisterFunction{0x1403115B0, 0x1403EDAE0}; + WEAK symbol Scr_GetFunction{0x140263E90, 0x1403318B0}; WEAK symbol VM_Execute{0x0, 0x1403F9E40}; - WEAK symbol Scr_ErrorInternal{0x0, 0x1403F80A0}; + WEAK symbol Scr_ErrorInternal{0x14031B840, 0x1403F80A0}; WEAK symbol SL_ConvertToString{0x140314850, 0x1403F0F10}; WEAK symbol SL_FindString{0x140314AF0, 0x1403F11C0}; @@ -202,8 +196,7 @@ namespace game WEAK symbol SV_SetConfigstring{0, 0x14043FCA0}; - WEAK symbol - Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0}; + WEAK symbol Sys_BuildAbsPath{0x14037BBE0, 0x1404CC7E0}; WEAK symbol Sys_CreateFile{0x14037BCA0, 0x1404CC8A0}; WEAK symbol Sys_Error{0x14038C770, 0x1404D6260}; WEAK symbol Sys_FileExists{0x14038C810, 0x1404D6310}; @@ -224,6 +217,8 @@ namespace game WEAK symbol PMem_AllocFromSource_NoDebug{0x1403775F0, 0x1404C7BA0}; WEAK symbol Hunk_AllocateTempMemoryHighInternal{0x140369D60, 0x1404B68B0}; + WEAK symbol LargeLocalResetToMark{0x140369C40, 0x1404B6790}; + WEAK symbol longjmp{0x14059C5C0, 0x1406FD930}; WEAK symbol _setjmp{0x14059CD00, 0x1406FE070}; @@ -246,6 +241,8 @@ namespace game WEAK symbol g_script_error{0x14A1917B0, 0x1487FA0C0}; WEAK symbol g_classMap{0x14080A840, 0x1409BE1B0}; + WEAK symbol com_codeTimeScale{0x0, 0x147B754EC}; + WEAK symbol scr_VarGlob{0x149B1D680, 0x148185F80}; WEAK symbol scr_VmPub{0x14A1938C0, 0x1487FC1C0}; WEAK symbol scr_function_stack{0x14A19DE40, 0x148806740}; diff --git a/src/common/utils/properties.cpp b/src/common/utils/properties.cpp new file mode 100644 index 0000000..8c85bfd --- /dev/null +++ b/src/common/utils/properties.cpp @@ -0,0 +1,25 @@ +#include "properties.hpp" + +#include + +#include + +namespace utils::properties +{ + std::filesystem::path get_appdata_path() + { + PWSTR path; + if (!SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path))) + { + throw std::runtime_error("Failed to read APPDATA path!"); + } + + auto _ = gsl::finally([&path] + { + CoTaskMemFree(path); + }); + + static auto appdata = std::filesystem::path(path) / "alterware"; + return appdata; + } +} diff --git a/src/common/utils/properties.hpp b/src/common/utils/properties.hpp new file mode 100644 index 0000000..92aff82 --- /dev/null +++ b/src/common/utils/properties.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +namespace utils::properties +{ + std::filesystem::path get_appdata_path(); +} From 4dd9eb2419687e7db908ec4110c152285214dccc Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Sat, 11 Jan 2025 10:37:10 +0100 Subject: [PATCH 2/5] maint: January Update Co-authored-by: Anomaly --- deps/GSL | 2 +- deps/gsc-tool | 2 +- deps/libtomcrypt | 2 +- src/client/component/auth.cpp | 88 ++++++++++++++++++++++----- src/client/component/colors.cpp | 26 +++++--- src/client/component/map_rotation.cpp | 2 +- src/client/component/rcon.cpp | 28 +++++---- src/client/component/security.cpp | 33 ++++++++++ src/client/game/symbols.hpp | 2 + src/common/utils/string.cpp | 4 +- 10 files changed, 146 insertions(+), 43 deletions(-) diff --git a/deps/GSL b/deps/GSL index b8ac820..355982d 160000 --- a/deps/GSL +++ b/deps/GSL @@ -1 +1 @@ -Subproject commit b8ac820fe1efde9c63ca290f4ae8670f938a7dae +Subproject commit 355982daf6c54ccb11bef8a1c511be2622dec402 diff --git a/deps/gsc-tool b/deps/gsc-tool index 9ff3b87..e4cb6a7 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit 9ff3b871ef59b14eb8b32d258bf18347e14a3e18 +Subproject commit e4cb6a7819726d9ea33cd6a52f0dcd9382258b47 diff --git a/deps/libtomcrypt b/deps/libtomcrypt index c900951..2e9f2b5 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit c900951dab1bb94bab803fc57688dac18e3b71f9 +Subproject commit 2e9f2b5e44dd498dba60e5175c6c7effa5ff3728 diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index a3b9781..8953001 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -36,11 +36,8 @@ namespace auth auto hw_profile_path = (utils::properties::get_appdata_path() / "s1-guid.dat").generic_string(); if (utils::io::file_exists(hw_profile_path)) { - std::string hw_profile_info; - if (utils::io::read_file(hw_profile_path, &hw_profile_info) && !hw_profile_info.empty()) - { - return hw_profile_info; - } + // Migration + utils::io::remove_file(hw_profile_path); } HW_PROFILE_INFO info; @@ -50,8 +47,6 @@ namespace auth } auto hw_profile_info = std::string{ info.szHwProfileGuid, sizeof(info.szHwProfileGuid) }; - utils::io::write_file(hw_profile_path, hw_profile_info); - return hw_profile_info; } @@ -62,7 +57,7 @@ namespace auth DATA_BLOB data_in{}, data_out{}; data_in.pbData = reinterpret_cast(input.data()); data_in.cbData = static_cast(input.size()); - if(CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE) + if (CryptProtectData(&data_in, nullptr, nullptr, nullptr, nullptr, CRYPTPROTECT_LOCAL_MACHINE, &data_out) != TRUE) { return {}; } @@ -91,9 +86,72 @@ namespace auth return entropy; } - utils::cryptography::ecc::key& get_key() + bool load_key(utils::cryptography::ecc::key& key) { - static auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy()); + std::string data{}; + + auto key_path = (utils::properties::get_appdata_path() / "s1-private.key").generic_string(); + if (!utils::io::read_file(key_path, &data)) + { + return false; + } + + key.deserialize(data); + if (!key.is_valid()) + { + console::error("Loaded key is invalid!\n"); + return false; + } + + return true; + } + + utils::cryptography::ecc::key generate_key() + { + auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy()); + if (!key.is_valid()) + { + throw std::runtime_error("Failed to generate cryptographic key!"); + } + + auto key_path = (utils::properties::get_appdata_path() / "s1-private.key").generic_string(); + if (!utils::io::write_file(key_path, key.serialize())) + { + console::error("Failed to write cryptographic key!\n"); + } + + console::info("Generated cryptographic key: %llX\n", key.get_hash()); + return key; + } + + utils::cryptography::ecc::key load_or_generate_key() + { + utils::cryptography::ecc::key key{}; + if (load_key(key)) + { + console::info("Loaded cryptographic key: %llX\n", key.get_hash()); + return key; + } + + return generate_key(); + } + + utils::cryptography::ecc::key get_key_internal() + { + auto key = load_or_generate_key(); + + auto key_path = (utils::properties::get_appdata_path() / "s1-public.key").generic_string(); + if (!utils::io::write_file(key_path, key.get_public_key())) + { + console::error("Failed to write public key!\n"); + } + + return key; + } + + const utils::cryptography::ecc::key& get_key() + { + static auto key = get_key_internal(); return key; } @@ -101,7 +159,7 @@ namespace auth { std::string connect_string(format, len); game::SV_Cmd_TokenizeString(connect_string.data()); - const auto _ = gsl::finally([]() + const auto _0 = gsl::finally([]() { game::SV_Cmd_EndTokenizedString(); }); @@ -124,7 +182,7 @@ namespace auth proto::network::connect_info info; info.set_publickey(get_key().get_public_key()); - info.set_signature(sign_message(get_key(), challenge)); + info.set_signature(utils::cryptography::ecc::sign_message(get_key(), challenge)); info.set_infostring(connect_string); network::send(*adr, "connect", info.SerializeAsString()); @@ -166,14 +224,14 @@ namespace auth utils::cryptography::ecc::key key; key.set(info.publickey()); - const auto xuid = strtoull(steam_id.data(), nullptr, 16); + const auto xuid = std::strtoull(steam_id.data(), nullptr, 16); if (xuid != key.get_hash()) { network::send(*from, "error", utils::string::va("XUID doesn't match the certificate: %llX != %llX", xuid, key.get_hash()), '\n'); return; } - if (!key.is_valid() || !verify_message(key, challenge, info.signature())) + if (!key.is_valid() || !utils::cryptography::ecc::verify_message(key, challenge, info.signature())) { network::send(*from, "error", "Challenge signature was invalid!", '\n'); return; @@ -233,7 +291,7 @@ namespace auth utils::hook::call(0x140208C54, send_connect_data_stub); } - command::add("guid", [] + command::add("guid", []() -> void { console::info("Your guid: %llX\n", steam::SteamUser()->GetSteamID().bits); }); diff --git a/src/client/component/colors.cpp b/src/client/component/colors.cpp index e918a4f..b4b7079 100644 --- a/src/client/component/colors.cpp +++ b/src/client/component/colors.cpp @@ -82,7 +82,16 @@ namespace colors void com_clean_name_stub(const char* in, char* out, const int out_size) { - strncpy_s(out, out_size, in, _TRUNCATE); + // Check that the name is at least 3 char without colors + char name[32]{}; + + game::I_strncpyz(out, in, std::min(out_size, sizeof(name))); + + utils::string::strip(out, name, std::strlen(out) + 1); + if (std::strlen(name) < 3) + { + game::I_strncpyz(out, "UnnamedPlayer", std::min(out_size, sizeof(name))); + } } char* i_clean_str_stub(char* string) @@ -92,14 +101,12 @@ namespace colors return string; } - size_t get_client_name_stub(const int local_client_num, const int index, char* buf, const int size, - const size_t unk, const size_t unk2) + int cl_get_client_name_and_clan_tag_stub(const int local_client_num, const int index, char* name_buf, const int name_size, char* clan_tag_buf, int clan_tag_size) { - // CL_GetClientName (CL_GetClientNameAndClantag?) - const auto result = reinterpret_cast(0x140213E60)( - local_client_num, index, buf, size, unk, unk2); + // CL_GetClientNameAndClanTag -> CL_GetClientNameAndClanTagColorize + const auto result = utils::hook::invoke(0x140213E60, local_client_num, index, name_buf, name_size, clan_tag_buf, clan_tag_size); - utils::string::strip(buf, buf, static_cast(size)); + utils::string::strip(name_buf, name_buf, name_size); return result; } @@ -148,10 +155,11 @@ namespace colors if (!game::environment::is_sp()) { // allows colored name in-game - utils::hook::jump(0x1404C9510, com_clean_name_stub); + utils::hook::call(0x1402DBD28, com_clean_name_stub); + utils::hook::call(0x1402DBD6D, com_clean_name_stub); // don't apply colors to overhead names - utils::hook::call(0x1401891B0, get_client_name_stub); + utils::hook::call(0x1401891B0, cl_get_client_name_and_clan_tag_stub); // patch I_CleanStr utils::hook::jump(0x1404C99A0, i_clean_str_stub); diff --git a/src/client/component/map_rotation.cpp b/src/client/component/map_rotation.cpp index f5f748b..38e3d74 100644 --- a/src/client/component/map_rotation.cpp +++ b/src/client/component/map_rotation.cpp @@ -113,7 +113,7 @@ namespace map_rotation console::error("%s: %s contains invalid data!\n", ex.what(), sv_map_rotation->name); } #ifdef _DEBUG - console::info("dedicated_rotation size after parsing is '%zu'", dedicated_rotation.get_entries_size()); + console::info("dedicated_rotation size after parsing is '%zu'\n", dedicated_rotation.get_entries_size()); #endif } diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp index 8cbb4fa..d0fdf45 100644 --- a/src/client/component/rcon.cpp +++ b/src/client/component/rcon.cpp @@ -80,23 +80,25 @@ namespace rcon { const auto client = &game::mp::svs_clients[i]; + if (client->header.state == game::CA_DISCONNECTED) + { + continue; + } + char clean_name[32]{}; strncpy_s(clean_name, client->name, _TRUNCATE); game::I_CleanStr(clean_name); - if (client->header.state > game::CA_DISCONNECTED) - { - buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n", - i, - game::G_GetClientScore(i), - game::SV_BotIsBot(i) ? "Yes" : "No", - (client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)), - game::SV_GetGuid(i), - clean_name, - network::net_adr_to_string(client->header.remoteAddress), - client->header.remoteAddress.port) - ); - } + buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n", + i, + game::G_GetClientScore(i), + game::SV_BotIsBot(i) ? "Yes" : "No", + (client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)), + game::SV_GetGuid(i), + clean_name, + network::net_adr_to_string(client->header.remoteAddress), + client->header.remoteAddress.port) + ); } return buffer; diff --git a/src/client/component/security.cpp b/src/client/component/security.cpp index b0bb0e9..fdc4f08 100644 --- a/src/client/component/security.cpp +++ b/src/client/component/security.cpp @@ -10,6 +10,8 @@ namespace security { namespace { + utils::hook::detour ui_replace_directive_hook; + void set_cached_playerdata_stub(const int localclient, const int index1, const int index2) { if (index1 >= 0 && index1 < 18 && index2 >= 0 && index2 < 42) @@ -53,6 +55,35 @@ namespace security utils::hook::invoke(0x14043AA90, client, msg); } + + void ui_replace_directive_stub(const int local_client_num, const char* src_string, char* dst_string, const int dst_buffer_size) + { + assert(src_string); + if (!src_string) + { + return; + } + + assert(dst_string); + if (!dst_string) + { + return; + } + + assert(dst_buffer_size > 0); + if (dst_buffer_size <= 0) + { + return; + } + + constexpr std::size_t MAX_HUDELEM_TEXT_LEN = 0x100; + if (std::strlen(src_string) > MAX_HUDELEM_TEXT_LEN) + { + return; + } + + ui_replace_directive_hook.invoke(local_client_num, src_string, dst_string, dst_buffer_size); + } } class component final : public component_interface @@ -70,6 +101,8 @@ namespace security // It is possible to make the server hang if left unchecked utils::hook::call(0x140443051, sv_execute_client_message_stub); + + ui_replace_directive_hook.create(0x1404A9640, ui_replace_directive_stub); } }; } diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 44e014a..426712c 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -219,6 +219,8 @@ namespace game WEAK symbol LargeLocalResetToMark{0x140369C40, 0x1404B6790}; + WEAK symbol I_strncpyz{0x1403793B0, 0x1404C9E60}; + WEAK symbol longjmp{0x14059C5C0, 0x1406FD930}; WEAK symbol _setjmp{0x14059CD00, 0x1406FE070}; diff --git a/src/common/utils/string.cpp b/src/common/utils/string.cpp index f8129d3..8a4c1be 100644 --- a/src/common/utils/string.cpp +++ b/src/common/utils/string.cpp @@ -109,13 +109,13 @@ namespace utils::string void strip(const char* in, char* out, size_t max) { - if (!in || !out) return; + if (!in || !out || !max) return; max--; auto current = 0u; while (*in != 0 && current < max) { - const auto color_index = (*(in + 1) - 48) >= 0xC ? 7 : (*(in + 1) - 48); + const auto color_index = (static_cast(*(in + 1) - 48)) >= 0xC ? 7 : (*(in + 1) - 48); if (*in == '^' && (color_index != 7 || *(in + 1) == '7')) { From 6217a48a0df4f74ef5ba524a99b38c48a2a27787 Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Thu, 30 Jan 2025 13:22:45 +0100 Subject: [PATCH 3/5] maint: update --- deps/gsc-tool | 2 +- deps/libtomcrypt | 2 +- src/client/component/bots.cpp | 1 - src/client/component/command.cpp | 32 ++-- src/client/component/dedicated.cpp | 34 +--- src/client/component/dvar_cheats.cpp | 4 +- src/client/component/filesystem.cpp | 18 +- src/client/component/gsc/script_loading.cpp | 2 +- src/client/component/lui.cpp | 4 +- src/client/component/party.cpp | 23 ++- src/client/component/patches.cpp | 7 +- src/client/component/rcon.cpp | 4 +- src/client/component/ui_scripting.cpp | 21 +++ src/client/game/engine/sv_game.cpp | 181 ++++++++++++++++++++ src/client/game/engine/sv_game.hpp | 7 + src/client/game/structs.hpp | 167 ++++++++++++++++-- src/client/game/symbols.hpp | 4 +- src/client/std_include.hpp | 1 + 18 files changed, 419 insertions(+), 95 deletions(-) create mode 100644 src/client/game/engine/sv_game.cpp create mode 100644 src/client/game/engine/sv_game.hpp diff --git a/deps/gsc-tool b/deps/gsc-tool index e4cb6a7..616595f 160000 --- a/deps/gsc-tool +++ b/deps/gsc-tool @@ -1 +1 @@ -Subproject commit e4cb6a7819726d9ea33cd6a52f0dcd9382258b47 +Subproject commit 616595f8e0dd62d6d57ed7a4ef7635a4c65e5dde diff --git a/deps/libtomcrypt b/deps/libtomcrypt index 2e9f2b5..54f0456 160000 --- a/deps/libtomcrypt +++ b/deps/libtomcrypt @@ -1 +1 @@ -Subproject commit 2e9f2b5e44dd498dba60e5175c6c7effa5ff3728 +Subproject commit 54f0456559c8f64b93273a7c71c88a46550eed4e diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 70656d7..c69be34 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -62,7 +62,6 @@ namespace bots return; } - // SV_BotGetRandomName const auto* const bot_name = game::SV_BotGetRandomName(); const auto* bot_ent = game::SV_AddBot(bot_name); if (bot_ent) diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index d41cd1e..1f0c43d 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -1,14 +1,13 @@ #include #include "loader/component_loader.hpp" - #include "game/game.hpp" +#include "game/engine/sv_game.hpp" #include "game/dvars.hpp" #include "command.hpp" #include "console.hpp" -#include "game_console.hpp" -#include "scheduler.hpp" #include "fastfiles.hpp" +#include "game_console.hpp" #include #include @@ -236,15 +235,13 @@ namespace command { if (!dvars::sv_cheats->current.enabled) { - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - "f \"Cheats are not enabled on this server\""); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"Cheats are not enabled on this server\""); return false; } if (ent->health < 1) { - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - "f \"You must be alive to use this command\""); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You must be alive to use this command\""); return false; } @@ -582,8 +579,7 @@ namespace command ent->flags ^= game::FL_GODMODE; - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off")); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"godmode %s\"", (ent->flags & game::FL_GODMODE) ? "^2on" : "^1off")); }); add_sv("demigod", [](game::mp::gentity_s* ent, const params_sv&) @@ -593,8 +589,7 @@ namespace command ent->flags ^= game::FL_DEMI_GODMODE; - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off")); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"demigod mode %s\"", (ent->flags & game::FL_DEMI_GODMODE) ? "^2on" : "^1off")); }); add_sv("notarget", [](game::mp::gentity_s* ent, const params_sv&) @@ -604,8 +599,7 @@ namespace command ent->flags ^= game::FL_NOTARGET; - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off")); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"notarget %s\"", (ent->flags & game::FL_NOTARGET) ? "^2on" : "^1off")); }); add_sv("noclip", [](game::mp::gentity_s* ent, const params_sv&) @@ -615,8 +609,7 @@ namespace command ent->client->flags ^= 1; - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off")); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"noclip %s\"", ent->client->flags & 1 ? "^2on" : "^1off")); }); add_sv("ufo", [](game::mp::gentity_s* ent, const params_sv&) @@ -626,8 +619,7 @@ namespace command ent->client->flags ^= 2; - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off")); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, utils::string::va("f \"ufo %s\"", ent->client->flags & 2 ? "^2on" : "^1off")); }); add_sv("give", [](game::mp::gentity_s* ent, const params_sv& params) @@ -637,8 +629,7 @@ namespace command if (params.size() < 2) { - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - "f \"You did not specify a weapon name\""); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); return; } @@ -661,8 +652,7 @@ namespace command if (params.size() < 2) { - game::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, - "f \"You did not specify a weapon name\""); + game::engine::SV_GameSendServerCommand(ent->s.number, game::SV_CMD_RELIABLE, "f \"You did not specify a weapon name\""); return; } diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index beef80d..ccc66ad 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/engine/sv_game.hpp" #include "console.hpp" #include "scheduler.hpp" @@ -99,8 +100,7 @@ namespace dedicated for (const auto& command : queue) { - game::Cbuf_AddText(0, command.data()); - game::Cbuf_AddText(0, "\n"); + command::execute(command); } } @@ -123,36 +123,10 @@ namespace dedicated void kill_server() { - for (auto i = 0; i < *game::mp::svs_numclients; ++i) - { - if (game::mp::svs_clients[i].header.state >= 3) - { - game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE, - utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); - } - } + game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); com_quit_f_hook.invoke(); } - - void sys_error_stub(const char* msg, ...) - { - char buffer[2048]{}; - - va_list ap; - va_start(ap, msg); - - vsnprintf_s(buffer, _TRUNCATE, msg, ap); - - va_end(ap); - - scheduler::once([] - { - command::execute("map_rotate"); - }, scheduler::main, 3s); - - game::Com_Error(game::ERR_DROP, "%s", buffer); - } } void initialize() @@ -190,8 +164,6 @@ namespace dedicated // Don't allow sv_hostname to be changed by the game dvars::disable::set_string("sv_hostname"); - // Stop crashing from sys_errors - utils::hook::jump(0x1404D6260, sys_error_stub); // Hook R_SyncGpu utils::hook::jump(0x1405A7630, sync_gpu_stub); diff --git a/src/client/component/dvar_cheats.cpp b/src/client/component/dvar_cheats.cpp index b105be0..1e972b0 100644 --- a/src/client/component/dvar_cheats.cpp +++ b/src/client/component/dvar_cheats.cpp @@ -1,7 +1,7 @@ #include #include "loader/component_loader.hpp" - #include "game/game.hpp" +#include "game/engine/sv_game.hpp" #include "game/dvars.hpp" #include "console.hpp" @@ -130,7 +130,7 @@ namespace dvar_cheats const auto* dvar = game::Scr_GetString(0); // grab the original dvar again since it's never stored on stack const auto* command = utils::string::va("q %s \"%s\"", dvar, value); - game::SV_GameSendServerCommand(entity_num, game::SV_CMD_RELIABLE, command); + game::engine::SV_GameSendServerCommand(static_cast(entity_num), game::SV_CMD_RELIABLE, command); } const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a) diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index 9196af1..3d22ef2 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -64,20 +64,22 @@ namespace filesystem void startup() { - register_path("s1"); + const auto base = std::filesystem::current_path(); + + register_path(base / "s1"); register_path(get_binary_directory() + "\\data"); - if (get_binary_directory() != std::filesystem::current_path()) + if (get_binary_directory() != base) { - register_path(std::filesystem::current_path() / "data"); + register_path(base / "data"); } // game's search paths - register_path("devraw"); - register_path("devraw_shared"); - register_path("raw_shared"); - register_path("raw"); - register_path("main"); + register_path(base / "devraw"); + register_path(base / "devraw_shared"); + register_path(base / "raw_shared"); + register_path(base / "raw"); + register_path(base / "main"); } void check_for_startup() diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index 83cfacc..7bc80bd 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -419,7 +419,7 @@ namespace gsc public: void post_load() override { - gsc_ctx = std::make_unique(); + gsc_ctx = std::make_unique(xsk::gsc::instance::server); } void post_unpack() override diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp index 7aa9a9e..320d5fa 100644 --- a/src/client/component/lui.cpp +++ b/src/client/component/lui.cpp @@ -29,7 +29,7 @@ namespace lui { if (params.size() <= 1) { - console::info("usage: lui_open \n"); + console::info("USAGE: %s \n", params.get(0)); return; } @@ -40,7 +40,7 @@ namespace lui { if (params.size() <= 1) { - console::info("usage: lui_open_popup \n"); + console::info("USAGE: %s \n", params.get(0)); return; } diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 15905c8..1749047 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/engine/sv_game.hpp" #include "game/dvars.hpp" #include "party.hpp" @@ -442,7 +443,7 @@ namespace party { scheduler::once([i, reason] { - game::SV_KickClientNum(i, reason.data()); + game::SV_KickClientNum(i, reason.c_str()); }, scheduler::pipeline::server); } return; @@ -456,7 +457,7 @@ namespace party scheduler::once([client_num, reason]() { - game::SV_KickClientNum(client_num, reason.data()); + game::SV_KickClientNum(client_num, reason.c_str()); }, scheduler::pipeline::server); }); @@ -476,9 +477,8 @@ namespace party const auto message = params.join(2); const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; - game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, - utils::string::va("%c \"%s: %s\"", 84, name, message.data())); - printf("%s -> %i: %s\n", name, client_num, message.data()); + game::engine::SV_GameSendServerCommand(static_cast(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str())); + printf("%s -> %i: %s\n", name, client_num, message.c_str()); }); command::add("tellraw", [](const command::params& params) @@ -491,9 +491,8 @@ namespace party const auto client_num = atoi(params.get(1)); const auto message = params.join(2); - game::SV_GameSendServerCommand(client_num, game::SV_CMD_CAN_IGNORE, - utils::string::va("%c \"%s\"", 84, message.data())); - printf("%i: %s\n", client_num, message.data()); + game::engine::SV_GameSendServerCommand(static_cast(client_num), game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str())); + printf("%i: %s\n", client_num, message.c_str()); }); command::add("say", [](const command::params& params) @@ -506,9 +505,8 @@ namespace party const auto message = params.join(1); const auto* const name = game::Dvar_FindVar("sv_sayName")->current.string; - game::SV_GameSendServerCommand( - -1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.data())); - printf("%s: %s\n", name, message.data()); + game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s: %s\"", 84, name, message.c_str())); + printf("%s: %s\n", name, message.c_str()); }); command::add("sayraw", [](const command::params& params) @@ -520,8 +518,7 @@ namespace party const auto message = params.join(1); - game::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, - utils::string::va("%c \"%s\"", 84, message.data())); + game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("%c \"%s\"", 84, message.c_str())); printf("%s\n", message.data()); }); diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 4cc58a1..7cbfdbe 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/engine/sv_game.hpp" #include "game/dvars.hpp" #include "command.hpp" @@ -178,7 +179,7 @@ namespace patches const auto client = &game::mp::svs_clients[ent->s.number]; // 22 => "end_game" - if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK) + if (menu_id == 22 && client->header.netchan.remoteAddress.type != game::NA_LOOPBACK) { return; } @@ -239,6 +240,10 @@ namespace patches static void patch_mp() { + // Bypass Arxan function + utils::hook::nop(0x14043E120, 9); + utils::hook::jump(0x14043E120, game::engine::SV_GameSendServerCommand); + // Use name dvar live_get_local_client_name_hook.create(0x1404D47F0, &live_get_local_client_name); diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp index d0fdf45..123dc80 100644 --- a/src/client/component/rcon.cpp +++ b/src/client/component/rcon.cpp @@ -96,8 +96,8 @@ namespace rcon (client->header.state == 2) ? "CNCT" : (client->header.state == 1) ? "ZMBI" : utils::string::va("%4i", game::SV_GetClientPing(i)), game::SV_GetGuid(i), clean_name, - network::net_adr_to_string(client->header.remoteAddress), - client->header.remoteAddress.port) + network::net_adr_to_string(client->header.netchan.remoteAddress), + client->header.netchan.remoteAddress.port) ); } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index c2e9393..0f7d3f9 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -206,6 +206,27 @@ namespace ui_scripting setup_functions(); lua["print"] = function(reinterpret_cast(0x14007CB70)); + + lua["directoryexists"] = [](const std::string& string) + { + return utils::io::directory_exists(string); + }; + + lua["listfiles"] = [](const std::string& string) + { + return utils::io::list_files(string); + }; + + lua["directoryisempty"] = [](const std::string& string) + { + return utils::io::directory_is_empty(string); + }; + + lua["fileexists"] = [](const std::string& string) + { + return utils::io::file_exists(string); + }; + lua["table"]["unpack"] = lua["unpack"]; lua["luiglobals"] = lua; diff --git a/src/client/game/engine/sv_game.cpp b/src/client/game/engine/sv_game.cpp new file mode 100644 index 0000000..b5dd968 --- /dev/null +++ b/src/client/game/engine/sv_game.cpp @@ -0,0 +1,181 @@ +#include +#include + +#include "sv_game.hpp" + +#include + +#include + +namespace game::engine +{ + char* SV_ExpandNewlines(char* in) + { + static char string[1024]; + + unsigned int l = 0; + while (*in && l < sizeof(string) - 3) + { + if (*in == '\n') + { + string[l++] = '\\'; + string[l++] = 'n'; + } + else + { + if (*in != '\x14' && *in != '\x15') + { + string[l++] = *in; + } + } + + ++in; + } + + string[l] = '\0'; + return string; + } + + void SV_CullIgnorableServerCommands(mp::client_t* client) + { + int to = client->reliableSent + 1; + for (int from = to; from <= client->reliableSequence; ++from) + { + int from_index = from & 0x7F; + assert(client->netBuf.reliableCommandInfo[from_index].time >= 0); + if (client->netBuf.reliableCommandInfo[from_index].type) + { + int to_index = to & 0x7F; + if (to_index != from_index) + { + client->netBuf.reliableCommandInfo[to_index] = client->netBuf.reliableCommandInfo[from_index]; + } + + ++to; + } + } + + client->reliableSequence = to - 1; + } + + void SV_DelayDropClient(mp::client_t* drop, const char* reason) + { + assert(drop); + assert(reason); + assert(drop->header.state != mp::CS_FREE); + if (drop->header.state == mp::CS_ZOMBIE) + { +#ifdef _DEBUG + console::info("(drop->dropReason) = %s", drop->dropReason); +#endif + } + else if (!drop->dropReason) + { + drop->dropReason = reason; + } + } + + void SV_AddServerCommand(mp::client_t* client, svscmd_type type, const char* cmd) + { + static_assert(offsetof(mp::client_t, netBuf.reliableCommandInfo[0].cmd) == 0xC44); + + if (client->testClient == TC_BOT) + { + return; + } + + if (client->reliableSequence - client->reliableAcknowledge < 64 && client->header.state == mp::CS_ACTIVE || (SV_CullIgnorableServerCommands(client), type)) + { + int len = static_cast(std::strlen(cmd)) + 1; + int to = SV_CanReplaceServerCommand(client, reinterpret_cast(cmd), len); + if (to < 0) + { + ++client->reliableSequence; + } + else + { + int from = to + 1; + while (from <= client->reliableSequence) + { + client->netBuf.reliableCommandInfo[to & 0x7F] = client->netBuf.reliableCommandInfo[from & 0x7F]; + ++from; + ++to; + } + } + + if (client->reliableSequence - client->reliableAcknowledge == 129) + { +#ifdef _DEBUG + console::info("===== pending server commands =====\n"); + int i = 0; + for (i = client->reliableAcknowledge + 1; i <= client->reliableSequence; ++i) + { + console::info("cmd %5d: %8d: %s\n", i, client->netBuf.reliableCommandInfo[i & 0x7F].time, client->netBuf.reliableCommandInfo[i & 0x7F].cmd); + } + console::info("cmd %5d: %8d: %s\n", i, *game::mp::serverTime, cmd); +#endif + NET_OutOfBandPrint(NS_SERVER, &client->header.netchan.remoteAddress, "disconnect"); + SV_DelayDropClient(client, "EXE_SERVERCOMMANDOVERFLOW"); + type = SV_CMD_RELIABLE; + cmd = utils::string::va("%c \"EXE_SERVERCOMMANDOVERFLOW\"", 'r'); + } + + int index = client->reliableSequence & 0x7F; + MSG_WriteReliableCommandToBuffer(cmd, client->netBuf.reliableCommandInfo[index].cmd, sizeof(client->netBuf.reliableCommandInfo[index].cmd)); + client->netBuf.reliableCommandInfo[index].time = *game::mp::serverTime; + client->netBuf.reliableCommandInfo[index].type = type; + } + } + + void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...) + { + mp::client_t* client; + int j, len; + va_list va; + + const auto server_command_buf_large = std::make_unique(0x20000); + + va_start(va, fmt); + len = vsnprintf_s(server_command_buf_large.get(), 0x20000, _TRUNCATE, fmt, va); + va_end(va); + + assert(len >= 0); + + if (cl) + { + SV_AddServerCommand(cl, type, server_command_buf_large.get()); + return; + } + + if (environment::is_dedi() && !std::strncmp(server_command_buf_large.get(), "print", 5)) + { + console::info("broadcast: %s\n", SV_ExpandNewlines(server_command_buf_large.get())); + } + + const auto* sv_maxclients = Dvar_FindVar("sv_maxclients"); + for (j = 0, client = mp::svs_clients; j < sv_maxclients->current.integer; j++, client++) + { + if (client->header.state < mp::CS_CLIENTLOADING) + { + continue; + } + + SV_AddServerCommand(client, type, server_command_buf_large.get()); + } + } + + void SV_GameSendServerCommand(char clientNum, svscmd_type type, const char* text) + { + [[maybe_unused]] const auto* sv_maxclients = Dvar_FindVar("sv_maxclients"); + + if (clientNum == -1) + { + SV_SendServerCommand(nullptr, type, "%s", text); + return; + } + + assert(sv_maxclients->current.integer >= 1 && sv_maxclients->current.integer <= 18); + assert(static_cast(clientNum) < sv_maxclients->current.unsignedInt); + SV_SendServerCommand(&mp::svs_clients[clientNum], type, "%s", text); + } +} diff --git a/src/client/game/engine/sv_game.hpp b/src/client/game/engine/sv_game.hpp new file mode 100644 index 0000000..32fb804 --- /dev/null +++ b/src/client/game/engine/sv_game.hpp @@ -0,0 +1,7 @@ +#pragma once + +namespace game::engine +{ + void SV_SendServerCommand(mp::client_t* cl, svscmd_type type, const char* fmt, ...); + void SV_GameSendServerCommand(char clientNum, svscmd_type type, const char* text); +} diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index dbef2e0..b7e7afa 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -855,10 +855,11 @@ namespace game int readcount; int bit; int lastEntityRef; - netsrc_t targetLocalNetID; int useZlib; }; + static_assert(sizeof(msg_t) == 0x38); + enum errorParm { ERR_FATAL = 0, @@ -1416,8 +1417,44 @@ namespace game static_assert(sizeof(pml_t) == 0x130); + struct netProfilePacket_t + { + int iTime; + int iSize; + int bFragment; + }; + + struct netProfileStream_t + { + netProfilePacket_t packets[60]; + int iCurrPacket; + int iBytesPerSecond; + int iLastBPSCalcTime; + int iCountedPackets; + int iCountedFragments; + int iFragmentPercentage; + int iLargestPacket; + int iSmallestPacket; + }; + + struct netProfileInfo_t + { + netProfileStream_t send; + netProfileStream_t recieve; + }; + namespace mp { + enum + { + CS_FREE = 0x0, + CS_ZOMBIE = 0x1, + CS_RECONNECTING = 0x2, + CS_CONNECTED = 0x3, + CS_CLIENTLOADING = 0x4, + CS_ACTIVE = 0x5, + }; + struct cachedSnapshot_t { int archivedFrame; @@ -1470,33 +1507,141 @@ namespace game { }; + struct netchan_t + { + int outgoingSequence; + netsrc_t sock; + int dropped; + int incomingSequence; + netadr_s remoteAddress; + int fragmentSequence; + int fragmentLength; + char* fragmentBuffer; + int fragmentBufferSize; + int unsentFragments; + int unsentFragmentStart; + int unsentLength; + char* unsentBuffer; + int unsentBufferSize; + netProfileInfo_t prof; + }; + struct clientHeader_t { int state; - char __pad0[36]; - netadr_s remoteAddress; - }; // size = ? + int sendAsActive; + int deltaMessage; + int rateDelayed; + int hasAckedBaselineData; + int hugeSnapshotSent; + netchan_t netchan; + float predictedOrigin[3]; + int predictedOriginServerTime; + int migrationState; + unsigned int predictedVehicleSplineId; + int predictedVehicleTargetEntity; + float predictedVehicleOrigin[3]; + int predictedVehicleServerTime; + int ackedMessage[32]; + unsigned int ackedMessageCount; + int sentMessage[32]; + int wasKillcam[32]; + unsigned int sendMessageCount; + bool overrideDeltaMessage; + int overrideSequenceNumber; + int sequenceResume; + int isInKillcam; + }; + + struct svscmd_info_t + { + int time; + int type; + char cmd[1024]; + }; + + static_assert(sizeof(svscmd_info_t) == 0x408); + + struct client_net_buffers_t + { + svscmd_info_t reliableCommandInfo[128]; + char netchanOutgoingBuffer[131072]; + char netchanIncomingBuffer[2048]; + }; + + struct usercmd_s + { + int serverTime; + unsigned int buttons; + int angles[3]; + Weapon weapon; + Weapon offHand; + char forwardmove; + char rightmove; + unsigned __int16 airburstMarkDistance; + __int16 meleeChargeEnt; + unsigned __int8 meleeChargeDist; + char selectedLoc[2]; + unsigned __int8 selectedLocAngle; + char remoteControlAngles[2]; + char remoteControlMove[3]; + unsigned int sightedClientsMask; + unsigned __int16 spawnTraceEntIndex; + unsigned int sightedSpawnsMask[2]; + unsigned int partialSightedSpawnsMask[2]; + }; + + static_assert(sizeof(usercmd_s) == 0x44); struct client_t { clientHeader_t header; - char __pad0[3044]; + const char* dropReason; + char userinfo[0x400]; int reliableSequence; int reliableAcknowledge; - char __pad1[265864]; + int reliableSent; + int messageAcknowledge; + int largeCommandSequence; + int gamestateMessageNum; + int challenge; + client_net_buffers_t netBuf; + int cumulThinkTime; + int beginCmdIndex; + int currCmdIndex; + usercmd_s lastUsercmd; + usercmd_s cmds[8]; + int lastClientCommand; gentity_s* gentity; // 268976 - char name[32]; // 268984 - char __pad2[8]; + char name[32]; + int lastPacketTime; + int lastConnectTime; int nextSnapshotTime; // 269024 - char __pad3[544]; + char __pad3[532]; + int pureAuthentic; + unsigned int streamSyncWaitBits; + unsigned int streamSyncWaitTimeout; LiveClientDropType liveDropRequest; // 269572 char __pad4[24]; TestClientType testClient; // 269600 char __pad5[391700]; }; // size = 661304 + static_assert(offsetof(client_t, header.netchan.unsentFragments) == 0x54); + static_assert(offsetof(client_t, header.migrationState) == 0x660); + static_assert(offsetof(client_t, userinfo) == 0x820); + static_assert(offsetof(client_t, reliableSequence) == 0xC20); + static_assert(offsetof(client_t, reliableAcknowledge) == 0xC24); + static_assert(offsetof(client_t, reliableSent) == 0xC28); + static_assert(offsetof(client_t, messageAcknowledge) == 0xC2C); + static_assert(offsetof(client_t, largeCommandSequence) == 0xC30); + static_assert(offsetof(client_t, lastUsercmd) == 0x41848); + static_assert(offsetof(client_t, lastClientCommand) == 0x41AAC); static_assert(offsetof(client_t, gentity) == 0x41AB0); + static_assert(offsetof(client_t, name) == 0x41AB8); + static_assert(offsetof(client_t, lastPacketTime) == 0x41AD8); static_assert(offsetof(client_t, nextSnapshotTime) == 0x41AE0); + static_assert(offsetof(client_t, pureAuthentic) == 0x41CF8); static_assert(offsetof(client_t, liveDropRequest) == 0x41D04); static_assert(offsetof(client_t, testClient) == 0x41D20); static_assert(sizeof(client_t) == 0xA1738); @@ -1508,7 +1653,9 @@ namespace game { char __pad[56135]; int flags; // 56136 - }; + }; // Incomplete + + static_assert(offsetof(gclient_s, flags) == 56136); struct gentity_s { diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 426712c..6b35668 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -184,7 +184,7 @@ namespace game WEAK symbol SV_DropClient{0, 0x140438A30}; WEAK symbol SV_ExecuteClientCommand{0, 0x15121D8E6}; WEAK symbol SV_FastRestart{0, 0x1404374E0}; - WEAK symbol SV_GameSendServerCommand{0x1403F3A70, 0x14043E120}; + WEAK symbol SV_CanReplaceServerCommand{0x0, 0x140441A00}; WEAK symbol SV_GetGuid{0, 0x14043E1E0}; WEAK symbol SV_GetClientPing{0, 0x14043E1C0}; WEAK symbol SV_GetPlayerstateForClientNum{0x1403F3AB0, 0x14043E260}; @@ -221,6 +221,8 @@ namespace game WEAK symbol I_strncpyz{0x1403793B0, 0x1404C9E60}; + WEAK symbol MSG_WriteReliableCommandToBuffer{0x0, 0x1403E1090}; + WEAK symbol longjmp{0x14059C5C0, 0x1406FD930}; WEAK symbol _setjmp{0x14059CD00, 0x1406FE070}; diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index 0ab229c..5a5ba55 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include From 547948a3248dff89c55d7c7d89157636bf97c99f Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Mon, 3 Feb 2025 20:49:48 +0100 Subject: [PATCH 4/5] fix(dedicated): remove loreless patch --- src/client/component/dedicated.cpp | 102 ++++++++++++++++++++++------- src/client/component/party.cpp | 88 ------------------------- src/client/component/party.hpp | 1 - src/client/component/patches.cpp | 19 ++++++ src/client/game/symbols.hpp | 3 +- 5 files changed, 101 insertions(+), 112 deletions(-) diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index ccc66ad..18ed590 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -3,12 +3,14 @@ #include "game/game.hpp" #include "game/engine/sv_game.hpp" +#include "command.hpp" #include "console.hpp" +#include "dvars.hpp" +#include "network.hpp" #include "scheduler.hpp" #include "server_list.hpp" -#include "network.hpp" -#include "command.hpp" -#include "dvars.hpp" + +#include "component/gsc/script_extension.hpp" #include #include @@ -17,9 +19,6 @@ namespace dedicated { namespace { - utils::hook::detour gscr_set_dynamic_dvar_hook; - utils::hook::detour com_quit_f_hook; - const game::dvar_t* sv_lanOnly; void init_dedicated_server() @@ -109,23 +108,53 @@ namespace dedicated std::this_thread::sleep_for(1ms); } - void gscr_set_dynamic_dvar() + void sv_kill_server_f() { - auto s = game::Scr_GetString(0); - auto* dvar = game::Dvar_FindVar(s); - if (dvar && !std::strncmp("scr_", dvar->name, 4)) + game::Com_Shutdown("EXE_SERVERKILLED"); + } + + void start_map(const std::string& map_name) + { + if (game::Live_SyncOnlineDataFlags(0) > 32) { + scheduler::once([map_name]() + { + command::execute(std::format("map {}", map_name), false); + }, scheduler::pipeline::main, 1s); + return; } - gscr_set_dynamic_dvar_hook.invoke(); + if (!game::SV_MapExists(map_name.c_str())) + { + console::info("Map '%s' doesn't exist.\n", map_name.c_str()); + return; + } + + auto* current_mapname = game::Dvar_FindVar("mapname"); + if (current_mapname && utils::string::to_lower(current_mapname->current.string) == utils::string::to_lower(map_name) && (game::SV_Loaded() && !game::VirtualLobby_Loaded())) + { + console::info("Restarting map: %s\n", map_name.c_str()); + command::execute("map_restart", false); + return; + } + + console::info("Starting map: %s\n", map_name.c_str()); + + auto* gametype = game::Dvar_FindVar("g_gametype"); + if (gametype && gametype->current.string) + { + command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true); + } + + command::execute(utils::string::va("ui_mapname %s", map_name.c_str()), true); + + game::SV_StartMapForParty(0, map_name.c_str(), false, false); } - void kill_server() + void gscr_is_using_match_rules_data_stub() { - game::engine::SV_GameSendServerCommand(-1, game::SV_CMD_CAN_IGNORE, utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); - - com_quit_f_hook.invoke(); + game::Scr_AddInt(0); } } @@ -164,10 +193,12 @@ namespace dedicated // Don't allow sv_hostname to be changed by the game dvars::disable::set_string("sv_hostname"); - // Hook R_SyncGpu utils::hook::jump(0x1405A7630, sync_gpu_stub); + // Make GScr_IsUsingMatchRulesData return 0 so the game doesn't override the cfg + gsc::override_function("isusingmatchrulesdata", gscr_is_using_match_rules_data_stub); + utils::hook::jump(0x14020C6B0, init_dedicated_server); // delay startup commands until the initialization is done @@ -177,9 +208,6 @@ namespace dedicated utils::hook::call(0x1403CEC35, execute_console_command); utils::hook::nop(0x1403CEC4B, 5); - // patch GScr_SetDynamicDvar to behave better - gscr_set_dynamic_dvar_hook.create(0x140312D00, &gscr_set_dynamic_dvar); - utils::hook::nop(0x1404AE6AE, 5); // don't load config file utils::hook::nop(0x1403AF719, 5); // ^ utils::hook::set(0x1403D2490, 0xC3); // don't save config file @@ -278,7 +306,7 @@ namespace dedicated console::info("==================================\n"); // remove disconnect command - game::Cmd_RemoveCommand(reinterpret_cast(751)); + game::Cmd_RemoveCommand(751); execute_startup_command_queue(); execute_console_command_queue(); @@ -289,8 +317,38 @@ namespace dedicated command::add("heartbeat", send_heartbeat); }, scheduler::pipeline::main, 1s); - command::add("killserver", kill_server); - com_quit_f_hook.create(0x1403D08C0, &kill_server); + command::add("killserver", sv_kill_server_f); + + command::add("map", [](const command::params& argument) + { + if (argument.size() != 2) + { + return; + } + + start_map(argument[1]); + }); + + command::add("map_restart", []() + { + if (!game::SV_Loaded() || game::VirtualLobby_Loaded()) + { + return; + } + + *reinterpret_cast(0x1488692B0) = 1; // sv_map_restart + *reinterpret_cast(0x1488692B4) = 1; // sv_loadScripts + *reinterpret_cast(0x1488692B8) = 0; // sv_migrate + reinterpret_cast(0x140437460)(0); // SV_CheckLoadGame + }); + + command::add("fast_restart", []() + { + if (game::SV_Loaded() && !game::VirtualLobby_Loaded()) + { + game::SV_FastRestart(0); + } + }); } }; } diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 1749047..8f94790 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -227,64 +227,6 @@ namespace party network::send(target, "getInfo", connect_state.challenge); } - void start_map(const std::string& mapname) - { - if (game::Live_SyncOnlineDataFlags(0) > 32) - { - scheduler::once([=]() - { - command::execute("map " + mapname, false); - }, scheduler::pipeline::main, 1s); - } - else - { - if (!game::SV_MapExists(mapname.data())) - { - console::info("Map '%s' doesn't exist.\n", mapname.data()); - return; - } - - auto* current_mapname = game::Dvar_FindVar("mapname"); - if (current_mapname && utils::string::to_lower(current_mapname->current.string) == - utils::string::to_lower(mapname) && (game::SV_Loaded() && !game::VirtualLobby_Loaded())) - { - console::info("Restarting map: %s\n", mapname.data()); - command::execute("map_restart", false); - return; - } - - if (!game::environment::is_dedi()) - { - if (game::SV_Loaded()) - { - const auto* args = "Leave"; - game::UI_RunMenuScript(0, &args); - } - - perform_game_initialization(); - } - - console::info("Starting map: %s\n", mapname.data()); - - auto* gametype = game::Dvar_FindVar("g_gametype"); - if (gametype && gametype->current.string) - { - command::execute(utils::string::va("ui_gametype %s", gametype->current.string), true); - } - command::execute(utils::string::va("ui_mapname %s", mapname.data()), true); - - /*auto* maxclients = game::Dvar_FindVar("sv_maxclients"); - if (maxclients) - { - command::execute(utils::string::va("ui_maxclients %i", maxclients->current.integer), true); - command::execute(utils::string::va("party_maxplayers %i", maxclients->current.integer), true); - }*/ - - const auto* args = "StartServer"; - game::UI_RunMenuScript(0, &args); - } - } - int server_client_count() { return sv_maxclients; @@ -315,36 +257,6 @@ namespace party // enable custom kick reason in GScr_KickPlayer utils::hook::set(0x14032ED80, 0xEB); - command::add("map", [](const command::params& argument) - { - if (argument.size() != 2) - { - return; - } - - start_map(argument[1]); - }); - - command::add("map_restart", []() - { - if (!game::SV_Loaded() || game::VirtualLobby_Loaded()) - { - return; - } - *reinterpret_cast(0x1488692B0) = 1; // sv_map_restart - *reinterpret_cast(0x1488692B4) = 1; // sv_loadScripts - *reinterpret_cast(0x1488692B8) = 0; // sv_migrate - reinterpret_cast(0x140437460)(0); // SV_CheckLoadGame - }); - - command::add("fast_restart", []() - { - if (game::SV_Loaded() && !game::VirtualLobby_Loaded()) - { - game::SV_FastRestart(0); - } - }); - command::add("reconnect", [](const command::params& argument) { if (!connect_state.hostDefined) diff --git a/src/client/component/party.hpp b/src/client/component/party.hpp index 4c45014..f9d452a 100644 --- a/src/client/component/party.hpp +++ b/src/client/component/party.hpp @@ -5,7 +5,6 @@ namespace party void reset_connect_state(); void connect(const game::netadr_s& target); - void start_map(const std::string& mapname); void clear_sv_motd(); int server_client_count(); diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 7cbfdbe..b71cebc 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -20,6 +20,8 @@ namespace patches { namespace { + utils::hook::detour com_quit_f_hook; + utils::hook::detour sv_shutdown_hook; utils::hook::detour live_get_local_client_name_hook; const char* live_get_local_client_name() @@ -186,6 +188,20 @@ namespace patches cmd_lui_notify_server_hook.invoke(ent); } + + void sv_shutdown_stub(const char* finalmsg) + { + console::info("----- Server Shutdown -----\n"); + + sv_shutdown_hook.invoke(finalmsg); + } + + void com_quit_f_stub() + { + console::info("quitting...\n"); + + com_quit_f_hook.invoke(); + } } class component final : public component_interface @@ -313,6 +329,9 @@ namespace patches // Prevent clients from ending the game as non host by sending 'end_game' lui notification cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub); + + com_quit_f_hook.create(0x1403D08C0, com_quit_f_stub); + sv_shutdown_hook.create(0x140440170, sv_shutdown_stub); } static void patch_sp() diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 6b35668..3ff18aa 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -23,10 +23,11 @@ namespace game WEAK symbol Com_GetCurrentCoDPlayMode{0, 0x1404C9690}; WEAK symbol Com_SetSlowMotion{0, 0x1403D19B0}; WEAK symbol Com_Quit_f{0x1402F9390, 0x1403D08C0}; + WEAK symbol Com_Shutdown{0x0, 0x1403D1BF0}; WEAK symbol Cmd_AddCommandInternal{0x1402EDDB0, 0x1403AF2C0}; WEAK symbol Cmd_ExecuteSingleCommand{0x1402EE350, 0x1403AF900}; - WEAK symbol Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0}; + WEAK symbol Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0}; WEAK symbol Cmd_TokenizeString{0x1402EEA30, 0x1403B0020}; WEAK symbol Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0}; From 5a4f1809867434186533b60ccac3b8a4ba5b903d Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Thu, 6 Feb 2025 12:55:05 +0100 Subject: [PATCH 5/5] fix: remove _s poison --- src/client/component/binding.cpp | 6 +-- src/client/component/console.cpp | 13 +++---- src/client/component/demonware.cpp | 9 ++--- src/client/component/game_console.cpp | 20 +++++----- src/client/component/game_log.cpp | 16 ++------ src/client/component/logger.cpp | 56 ++------------------------- src/client/component/party.cpp | 2 +- src/client/component/patches.cpp | 6 ++- src/client/component/rcon.cpp | 2 +- src/client/component/security.cpp | 37 ++++++++++++++++++ src/client/game/engine/sv_game.cpp | 2 +- src/client/game/symbols.hpp | 2 + src/client/main.cpp | 4 +- 13 files changed, 77 insertions(+), 98 deletions(-) diff --git a/src/client/component/binding.cpp b/src/client/component/binding.cpp index f88a0a6..645b010 100644 --- a/src/client/component/binding.cpp +++ b/src/client/component/binding.cpp @@ -30,8 +30,7 @@ namespace binding if (value && value < get_num_keys()) { - const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used), - "bind %s \"%s\"\n", key_button, game::command_whitelist[value]); + const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, game::command_whitelist[value]); if (len < 0) { @@ -45,8 +44,7 @@ namespace binding value -= get_num_keys(); if (static_cast(value) < custom_binds.size() && !custom_binds[value].empty()) { - const auto len = sprintf_s(&buffer[bytes_used], (buffer_size_align - bytes_used), - "bind %s \"%s\"\n", key_button, custom_binds[value].data()); + const auto len = game::Com_sprintf(&buffer[bytes_used], (buffer_size_align - bytes_used), "bind %s \"%s\"\n", key_button, custom_binds[value].data()); if (len < 0) { diff --git a/src/client/component/console.cpp b/src/client/component/console.cpp index a66101d..b11d3d7 100644 --- a/src/client/component/console.cpp +++ b/src/client/component/console.cpp @@ -42,7 +42,7 @@ namespace console { static thread_local char buffer[0x1000]; - const auto count = vsnprintf_s(buffer, _TRUNCATE, message, *ap); + const auto count = vsnprintf(buffer, sizeof(buffer), message, *ap); if (count < 0) return {}; return {buffer, static_cast(count)}; @@ -84,15 +84,14 @@ namespace console void print_stub(const char* fmt, ...) { + char buffer[4096]{}; + va_list ap; va_start(ap, fmt); - - char buffer[4096]{}; - const auto res = vsnprintf_s(buffer, _TRUNCATE, fmt, ap); - (void)res; - print_message(buffer); - + [[maybe_unused]] const auto res = vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); + + print_message(buffer); } void append_text(const char* text) diff --git a/src/client/component/demonware.cpp b/src/client/component/demonware.cpp index a07cbc6..cbbc7c2 100644 --- a/src/client/component/demonware.cpp +++ b/src/client/component/demonware.cpp @@ -348,15 +348,14 @@ namespace demonware return; } - char buffer[2048]; + char buffer[2048]{}; va_list ap; va_start(ap, msg); - - vsnprintf_s(buffer, _TRUNCATE, msg, ap); - printf("%s: %s\n", function, buffer); - + vsnprintf(buffer, sizeof(buffer), msg, ap); va_end(ap); + + printf("%s: %s\n", function, buffer); } void startup_dw() diff --git a/src/client/component/game_console.cpp b/src/client/component/game_console.cpp index 90e0167..9384ac4 100644 --- a/src/client/component/game_console.cpp +++ b/src/client/component/game_console.cpp @@ -63,7 +63,7 @@ namespace game_console void clear() { - strncpy_s(con.buffer, "", sizeof(con.buffer)); + game::I_strncpyz(con.buffer, "", sizeof(con.buffer)); con.cursor = 0; fixed_input = ""; @@ -249,7 +249,7 @@ namespace game_console dvars::con_inputDvarInactiveValueColor->current.vector, offset); } - strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64); + game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64); con.globals.may_auto_complete = true; } else if (matches.size() > 1) @@ -274,7 +274,7 @@ namespace game_console } } - strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64); + game::I_strncpyz(con.globals.auto_complete_choice, matches[0].data(), 64); con.globals.may_auto_complete = true; } } @@ -365,11 +365,11 @@ namespace game_console void print_internal(const char* fmt, ...) { - char va_buffer[0x200]{}; + char va_buffer[1024]{}; va_list ap; va_start(ap, fmt); - vsprintf_s(va_buffer, fmt, ap); + vsnprintf(va_buffer, sizeof(va_buffer), fmt, ap); va_end(ap); const auto formatted = std::string(va_buffer); @@ -425,7 +425,7 @@ namespace game_console con.buffer[1] = '\0'; } - strncat_s(con.buffer, con.globals.auto_complete_choice, 64); + game::I_strncat(con.buffer, sizeof(con.buffer), con.globals.auto_complete_choice); con.cursor = static_cast(std::string(con.buffer).length()); if (con.cursor != 254) @@ -550,7 +550,7 @@ namespace game_console if (history_index != -1) { - strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); + game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); con.cursor = static_cast(strlen(con.buffer)); } } @@ -565,7 +565,7 @@ namespace game_console if (history_index != -1) { - strncpy_s(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); + game::I_strncpyz(con.buffer, history.at(history_index).c_str(), sizeof(con.buffer)); con.cursor = static_cast(strlen(con.buffer)); } } @@ -720,7 +720,7 @@ namespace game_console con.output_visible = false; con.display_line_offset = 0; con.line_count = 0; - strncpy_s(con.buffer, "", sizeof(con.buffer)); + game::I_strncpyz(con.buffer, "", sizeof(con.buffer)); con.globals.x = 0.0f; con.globals.y = 0.0f; @@ -728,7 +728,7 @@ namespace game_console con.globals.font_height = 0.0f; con.globals.may_auto_complete = false; con.globals.info_line_count = 0; - strncpy_s(con.globals.auto_complete_choice, "", 64); + game::I_strncpyz(con.globals.auto_complete_choice, "", 64); // add clear command command::add("clear", [&]() diff --git a/src/client/component/game_log.cpp b/src/client/component/game_log.cpp index 23775aa..cdde815 100644 --- a/src/client/component/game_log.cpp +++ b/src/client/component/game_log.cpp @@ -10,7 +10,6 @@ #include "gsc/script_extension.hpp" -#include #include #include @@ -23,7 +22,7 @@ namespace game_log char buf[1024]{}; std::size_t out_chars = 0; - for (auto i = 0u; i < game::Scr_GetNumParam(); ++i) + for (std::uint32_t i = 0; i < game::Scr_GetNumParam(); ++i) { const auto* value = game::Scr_GetString(i); const auto len = std::strlen(value); @@ -34,7 +33,7 @@ namespace game_log break; } - strncat_s(buf, value, _TRUNCATE); + game::I_strncat(buf, sizeof(buf), value); } g_log_printf("%s", buf); @@ -53,18 +52,11 @@ namespace game_log va_list ap; va_start(ap, fmt); - - vsnprintf_s(buffer, _TRUNCATE, fmt, ap); - + vsnprintf(buffer, sizeof(buffer), fmt, ap); va_end(ap); const auto time = *game::level_time / 1000; - utils::io::write_file(log, utils::string::va("%3i:%i%i %s", - time / 60, - time % 60 / 10, - time % 60 % 10, - buffer - ), true); + utils::io::write_file(log, utils::string::va("%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buffer), true); } class component final : public component_interface diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index 60eb61d..abb5494 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -2,7 +2,6 @@ #include "loader/component_loader.hpp" #include "game/game.hpp" -#include "party.hpp" #include "console.hpp" #include @@ -11,57 +10,15 @@ namespace logger { namespace { - utils::hook::detour com_error_hook; - game::dvar_t* logger_dev = nullptr; - void print_error(const char* msg, ...) - { - char buffer[2048]{}; - va_list ap; - - va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); - va_end(ap); - - console::error("%s", buffer); - } - - void print_com_error(int, const char* msg, ...) - { - char buffer[2048]{}; - va_list ap; - - va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); - va_end(ap); - - console::error("%s", buffer); - } - - void com_error_stub(const int error, const char* msg, ...) - { - char buffer[2048]{}; - va_list ap; - - va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); - va_end(ap); - - console::error("Error: %s\n", buffer); - - party::clear_sv_motd(); // clear sv_motd on error if it exists - - com_error_hook.invoke(error, "%s", buffer); - } - void print_warning(const char* msg, ...) { char buffer[2048]{}; va_list ap; va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); + vsnprintf(buffer, sizeof(buffer), msg, ap); va_end(ap); console::warn("%s", buffer); @@ -73,7 +30,7 @@ namespace logger va_list ap; va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); + vsnprintf(buffer, sizeof(buffer), msg, ap); va_end(ap); console::info("%s", buffer); @@ -90,7 +47,7 @@ namespace logger va_list ap; va_start(ap, msg); - vsnprintf_s(buffer, _TRUNCATE, msg, ap); + vsnprintf(buffer, sizeof(buffer), msg, ap); va_end(ap); console::info("%s", buffer); @@ -157,13 +114,6 @@ namespace logger utils::hook::jump(0x140701A1C, print); } - if (!game::environment::is_sp()) - { - utils::hook::call(0x1404D8543, print_com_error); - } - - com_error_hook.create(game::Com_Error, com_error_stub); - logger_dev = game::Dvar_RegisterBool("logger_dev", false, game::DVAR_FLAG_SAVED); } }; diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 8f94790..81e9201 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -160,7 +160,7 @@ namespace party if (game::mp::g_entities[i].client) { char client_name[16] = {0}; - strncpy_s(client_name, game::mp::g_entities[i].client->name, sizeof(client_name)); + game::I_strncpyz(client_name, game::mp::g_entities[i].client->name, sizeof(client_name)); game::I_CleanStr(client_name); if (client_name == name) diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index b71cebc..28b5556 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -118,7 +118,7 @@ namespace patches char* db_read_raw_file_stub(const char* filename, char* buf, const int size) { std::string file_name = filename; - if (file_name.find(".cfg") == std::string::npos) + if (!file_name.ends_with(".cfg")) { file_name.append(".cfg"); } @@ -127,6 +127,7 @@ namespace patches if (file.exists()) { snprintf(buf, size, "%s\n", file.get_buffer().data()); + buf[size - 1] = '\0'; return buf; } @@ -327,6 +328,9 @@ namespace patches game::Dvar_RegisterInt("scr_game_spectatetype", 1, 0, 99, game::DVAR_FLAG_REPLICATED); + // Disable Com_Error in NET_SendPacket + utils::hook::nop(0x1404D8543, 5); + // Prevent clients from ending the game as non host by sending 'end_game' lui notification cmd_lui_notify_server_hook.create(0x1402E9390, cmd_lui_notify_server_stub); diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp index 123dc80..727e898 100644 --- a/src/client/component/rcon.cpp +++ b/src/client/component/rcon.cpp @@ -86,7 +86,7 @@ namespace rcon } char clean_name[32]{}; - strncpy_s(clean_name, client->name, _TRUNCATE); + game::I_strncpyz(clean_name, client->name, sizeof(clean_name)); game::I_CleanStr(clean_name); buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n", diff --git a/src/client/component/security.cpp b/src/client/component/security.cpp index fdc4f08..2152374 100644 --- a/src/client/component/security.cpp +++ b/src/client/component/security.cpp @@ -84,6 +84,36 @@ namespace security ui_replace_directive_hook.invoke(local_client_num, src_string, dst_string, dst_buffer_size); } + + int hud_elem_set_enum_string_stub(char* string, const char* format, ...) + { + va_list ap; + va_start(ap, format); + const auto len = vsnprintf(string, 0x800, format, ap); + va_end(ap); + + return len; + } + + int sv_add_bot_stub(char* string, const char* format, ...) + { + va_list ap; + va_start(ap, format); + const auto len = vsnprintf(string, 0x400, format, ap); + va_end(ap); + + return len; + } + + int sv_add_test_client_stub(char* string, const char* format, ...) + { + va_list ap; + va_start(ap, format); + const auto len = vsnprintf(string, 0x400, format, ap); + va_end(ap); + + return len; + } } class component final : public component_interface @@ -91,11 +121,18 @@ namespace security public: void post_unpack() override { + // sprinf + utils::hook::call(SELECT_VALUE(0x1402203DF, 0x1402F0BAF), hud_elem_set_enum_string_stub); + if (game::environment::is_sp()) return; // Patch vulnerability in PlayerCards_SetCachedPlayerData utils::hook::call(0x1401BB909, set_cached_playerdata_stub); + // sprinf + utils::hook::call(0x140439075, sv_add_bot_stub); + utils::hook::call(0x14043932A, sv_add_test_client_stub); + // Patch entity overflow utils::hook::jump(0x14044DE3A, assemble(remap_cached_entities_stub), true); diff --git a/src/client/game/engine/sv_game.cpp b/src/client/game/engine/sv_game.cpp index b5dd968..10ce196 100644 --- a/src/client/game/engine/sv_game.cpp +++ b/src/client/game/engine/sv_game.cpp @@ -136,7 +136,7 @@ namespace game::engine const auto server_command_buf_large = std::make_unique(0x20000); va_start(va, fmt); - len = vsnprintf_s(server_command_buf_large.get(), 0x20000, _TRUNCATE, fmt, va); + len = vsnprintf(server_command_buf_large.get(), 0x20000, fmt, va); va_end(va); assert(len >= 0); diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 3ff18aa..256f103 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -30,6 +30,7 @@ namespace game WEAK symbol Cmd_RemoveCommand{0x1402EE910, 0x1403AFEF0}; WEAK symbol Cmd_TokenizeString{0x1402EEA30, 0x1403B0020}; WEAK symbol Cmd_EndTokenizeString{0x1402EE000, 0x1403AF5B0}; + WEAK symbol Com_sprintf{0x140378E30, 0x1404C97B0}; WEAK symbol Conbuf_AppendText{0x14038F220, 0x1404D9040}; @@ -221,6 +222,7 @@ namespace game WEAK symbol LargeLocalResetToMark{0x140369C40, 0x1404B6790}; WEAK symbol I_strncpyz{0x1403793B0, 0x1404C9E60}; + WEAK symbol I_strncat{0x1403792E0, 0x1404C9D90}; WEAK symbol MSG_WriteReliableCommandToBuffer{0x0, 0x1403E1090}; diff --git a/src/client/main.cpp b/src/client/main.cpp index c0ad37d..d7d2686 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -64,9 +64,7 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) &exception_information, nullptr, nullptr)) { char buf[4096]{}; - sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", - exception_info->ExceptionRecord->ExceptionCode, - exception_info->ExceptionRecord->ExceptionAddress); + sprintf_s(buf, "An exception 0x%08X occurred at location 0x%p\n", exception_info->ExceptionRecord->ExceptionCode, exception_info->ExceptionRecord->ExceptionAddress); game::show_error(buf); }