diff --git a/README.md b/README.md index a80531f..5d38b12 100644 --- a/README.md +++ b/README.md @@ -183,10 +183,10 @@ This is a client modification for IW6! - Install [Premake5][premake5-link] and add it to your system PATH - Clone this repository using [Git][git-link] - Update the submodules using ``git submodule update --init --recursive`` -- Run Premake with this options ``premake5 vs2022`` (Visual Studio 2022). No other build systems are supported. -- Build project via solution file in `build\s1-mod.sln`. +- Run Premake with the option ``premake5 vs2022`` (Visual Studio 2022). No other build systems are supported. +- Build project via solution file in `build\iw6-mod.sln`. -Only x64 is supported. Do not attempt to build for Windows ARM 64. +Only the x64 platform is supported. Do not attempt to build for Windows ARM 64 or Win32. ### Premake arguments diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 2da44fd..21ba876 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -75,8 +75,8 @@ namespace bots return; } - auto* bot_name = game::SV_BotGetRandomName(); - auto* bot_ent = game::SV_AddBot(bot_name, 26, 62, 0); + const auto* bot_name = game::SV_BotGetRandomName(); + const auto* bot_ent = game::SV_AddBot(bot_name, 26, 62, 0); if (bot_ent) { spawn_bot(bot_ent->s.number); @@ -87,34 +87,13 @@ namespace bots volatile bool bot_names_received = false; std::vector bot_names; - const char* get_random_bot_name() - { - if (bot_names.empty()) - { - return get_bot_name_hook.invoke(); - } - - const auto index = std::rand() % bot_names.size(); - const auto& name = bot_names.at(index); - - return utils::string::va("%.*s", static_cast(name.size()), name.data()); - } - - bool should_update_bot_names() + bool should_use_remote_bot_names() { +#ifdef ALLOW_CUSTOM_BOT_NAMES return !filesystem::exists("bots.txt"); - } - - void update_bot_names() - { - bot_names_received = false; - - game::netadr_s master{}; - if (server_list::get_master_server(master)) - { - console::info("Getting bots...\n"); - network::send(master, "getbots"); - } +#else + return true; +#endif } void parse_bot_names_from_file() @@ -141,6 +120,37 @@ namespace bots bot_names.emplace_back(entry); } } + + const char* get_random_bot_name() + { + if (!bot_names_received && bot_names.empty()) + { + // last attempt to use custom names if they can be found + parse_bot_names_from_file(); + } + + if (bot_names.empty()) + { + return get_bot_name_hook.invoke(); + } + + const auto index = std::rand() % bot_names.size(); + const auto& name = bot_names.at(index); + + return utils::string::va("%.*s", static_cast(name.size()), name.data()); + } + + void update_bot_names() + { + bot_names_received = false; + + game::netadr_s master{}; + if (server_list::get_master_server(master)) + { + console::info("Getting bots...\n"); + network::send(master, "getbots"); + } + } } class component final : public component_interface @@ -159,23 +169,23 @@ namespace bots { if (!game::SV_Loaded()) return; - auto num_bots = 1; + std::size_t num_bots = 1; if (params.size() == 2) { - num_bots = atoi(params.get(1)); + num_bots = std::strtoul(params.get(1), nullptr, 10); } - num_bots = std::min(num_bots, *game::mp::svs_clientCount); + num_bots = std::min(num_bots, static_cast(*game::mp::svs_clientCount)); - console::info("Spawning %i %s\n", num_bots, (num_bots == 1 ? "bot" : "bots")); + console::info("Spawning %zu %s\n", num_bots, (num_bots == 1 ? "bot" : "bots")); - for (auto i = 0; i < num_bots; i++) + for (std::size_t i = 0; i < num_bots; ++i) { scheduler::once(add_bot, scheduler::pipeline::server, 100ms * i); } }); - if (should_update_bot_names()) + if (should_use_remote_bot_names()) { scheduler::on_game_initialized([]() { @@ -194,6 +204,7 @@ namespace bots if (server_list::get_master_server(master) && !bot_names_received && target == master) { bot_names = utils::string::split(data, '\n'); + console::info("Got %zu names from the master server\n", bot_names.size()); bot_names_received = true; } }); diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index a4263cf..03b951b 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -13,8 +13,6 @@ #include #include -#include - namespace dedicated { namespace @@ -105,12 +103,6 @@ namespace dedicated reinterpret_cast(0x14046F3B0)(); // SV_CheckLoadGame } - game::dvar_t* register_maxfps_stub(const char* name, int, int, int, unsigned int flags, - const char* desc) - { - return game::Dvar_RegisterInt(name, 0, 0, 0, game::DvarFlags::DVAR_FLAG_READ, desc); - } - void send_heartbeat() { if (sv_lanOnly->current.enabled) @@ -293,7 +285,7 @@ namespace dedicated //utils::hook::set(0x1402C89A0, 0xC3); // R_Init caller utils::hook::jump(0x1402C89A0, init_dedicated_server); - utils::hook::call(0x140413AD8, register_maxfps_stub); + dvars::override::register_int("com_maxfps", 0, 0, 0, game::DVAR_FLAG_READ); // delay startup commands until the initialization is done utils::hook::call(0x140412183, execute_startup_command); @@ -302,6 +294,10 @@ namespace dedicated utils::hook::call(0x140412FD3, execute_console_command); utils::hook::nop(0x140412FE9, 5); + // disable check on an unregistered relay dvar + utils::hook::nop(0x14041E9EB, 4); + utils::hook::set(0x14041E9EF, 0xEB); + utils::hook::nop(0x1404DDC2E, 5); // don't load config file utils::hook::set(0x140416100, 0xC3); // don't save config file utils::hook::set(0x1402E5830, 0xC3); // disable self-registration diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index 51c95be..28f0b03 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -18,7 +18,23 @@ namespace dvars bool value{}; }; + struct dvar_int : dvar_base + { + int value{}; + int min{}; + int max{}; + }; + + struct dvar_float : dvar_base + { + float value{}; + float min{}; + float max{}; + }; + utils::hook::detour dvar_register_bool_hook; + utils::hook::detour dvar_register_int_hook; + utils::hook::detour dvar_register_float_hook; template T* find_dvar(std::unordered_map& map, const std::string& name) @@ -35,6 +51,8 @@ namespace dvars namespace override { static std::unordered_map register_bool_overrides; + static std::unordered_map register_int_overrides; + static std::unordered_map register_float_overrides; void register_bool(const std::string& name, const bool value, const unsigned int flags) { @@ -43,6 +61,26 @@ namespace dvars values.flags = flags; register_bool_overrides[name] = values; } + + void register_int(const std::string& name, const int value, const int min, const int max, const unsigned int flags) + { + dvar_int values; + values.value = value; + values.min = min; + values.max = max; + values.flags = flags; + register_int_overrides[name] = std::move(values); + } + + void register_float(const std::string& name, const float value, const float min, const float max, const unsigned int flags) + { + dvar_float values; + values.value = value; + values.min = min; + values.max = max; + values.flags = flags; + register_float_overrides[name] = std::move(values); + } } std::string get_string(const std::string& dvar) @@ -68,17 +106,49 @@ namespace dvars return dvar_register_bool_hook.invoke(name, value, flags, description); } + game::dvar_t* dvar_register_int_stub(const char* name, int value, int min, int max, unsigned int flags, const char* description) + { + auto* var = find_dvar(override::register_int_overrides, name); + if (var) + { + value = var->value; + min = var->min; + max = var->max; + flags = var->flags; + } + + return dvar_register_int_hook.invoke(name, value, min, max, flags, description); + } + + game::dvar_t* dvar_register_float_stub(const char* name, float value, float min, float max, unsigned int flags, const char* description) + { + auto* var = find_dvar(override::register_float_overrides, name); + if (var) + { + value = var->value; + min = var->min; + max = var->max; + flags = var->flags; + } + + return dvar_register_float_hook.invoke(name, value, min, max, flags, description); + } + class component final : public component_interface { public: void post_unpack() override { dvar_register_bool_hook.create(game::Dvar_RegisterBool, &dvar_register_bool_stub); + dvar_register_int_hook.create(game::Dvar_RegisterInt, &dvar_register_int_stub); + dvar_register_float_hook.create(game::Dvar_RegisterFloat, &dvar_register_float_stub); } void pre_destroy() override { dvar_register_bool_hook.clear(); + dvar_register_int_hook.clear(); + dvar_register_float_hook.clear(); } }; } diff --git a/src/client/component/dvars.hpp b/src/client/component/dvars.hpp index 687f8b6..34d60f5 100644 --- a/src/client/component/dvars.hpp +++ b/src/client/component/dvars.hpp @@ -5,6 +5,8 @@ namespace dvars namespace override { void register_bool(const std::string& name, bool value, unsigned int flags); + void register_int(const std::string& name, int value, int min, int max, unsigned int flags); + void register_float(const std::string& name, float value, float min, float max, unsigned int flags); } std::string get_string(const std::string& dvar); diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index bcf2cf0..9bcac55 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -25,6 +25,12 @@ namespace filesystem return search_paths; } + const char* sys_default_install_path_stub() + { + static auto current_path = std::filesystem::current_path().string(); + return current_path.data(); + } + std::string get_binary_directory() { const auto dir = game_module::get_host_module().get_folder(); @@ -264,6 +270,10 @@ namespace filesystem // Set fs_basegame utils::hook::inject(SELECT_VALUE(0x14041C053, 0x1404DDA13), "iw6"); +#ifndef INJECT_HOST_AS_LIB + utils::hook::jump(SELECT_VALUE(0x1404351B0, 0x1404F96C0), sys_default_install_path_stub); +#endif + if (game::environment::is_sp()) { utils::hook::call(0x14041B744, fs_startup_stub); diff --git a/src/client/component/gsc/script_error.cpp b/src/client/component/gsc/script_error.cpp index 9585edf..9bd853c 100644 --- a/src/client/component/gsc/script_error.cpp +++ b/src/client/component/gsc/script_error.cpp @@ -71,10 +71,9 @@ namespace gsc return scripting::get_token(id); } - void get_unknown_function_error(const char* code_pos) + void get_unknown_function_error(char* code_pos) { - const auto function = find_function(code_pos); - if (function.has_value()) + if (const auto function = find_function(code_pos); function.has_value()) { const auto& pos = function.value(); unknown_function_error = std::format( @@ -97,7 +96,7 @@ namespace gsc ); } - void compile_error_stub(const char* code_pos, [[maybe_unused]] const char* msg) + void compile_error_stub(char* code_pos, [[maybe_unused]] const char* msg) { get_unknown_function_error(code_pos); game::Com_Error(game::ERR_DROP, "script link error\n%s", unknown_function_error.data()); diff --git a/src/client/component/gsc/script_extension.cpp b/src/client/component/gsc/script_extension.cpp index dd0634e..753a861 100644 --- a/src/client/component/gsc/script_extension.cpp +++ b/src/client/component/gsc/script_extension.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/dvars.hpp" #include "game/scripting/functions.hpp" @@ -8,7 +9,6 @@ #include #include "component/console.hpp" -#include "component/scripting.hpp" #include "component/notifies.hpp" #include "component/command.hpp" @@ -23,8 +23,6 @@ namespace gsc void* func_table[0x1000]; - const game::dvar_t* developer_script = nullptr; - namespace { #define RVA(ptr) static_cast(reinterpret_cast(ptr) - 0x140000000) @@ -135,7 +133,7 @@ namespace gsc void vm_error_stub(int mark_pos) { - if (!developer_script->current.enabled && !force_error_print) + if (!dvars::com_developer_script->current.enabled && !force_error_print) { utils::hook::invoke(0x1404E4D00, mark_pos); return; @@ -284,8 +282,6 @@ namespace gsc return; } - developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments"); - utils::hook::nop(0x14043BBBE + 5, 2); utils::hook::call(0x14043BBBE, vm_call_builtin_function); diff --git a/src/client/component/gsc/script_extension.hpp b/src/client/component/gsc/script_extension.hpp index 0c07e9c..c8cc2f4 100644 --- a/src/client/component/gsc/script_extension.hpp +++ b/src/client/component/gsc/script_extension.hpp @@ -4,8 +4,6 @@ namespace gsc { extern void* func_table[0x1000]; - extern const game::dvar_t* developer_script; - void add_function(const std::string& name, game::BuiltinFunction function); void scr_error(const char* error); diff --git a/src/client/component/gsc/script_loading.cpp b/src/client/component/gsc/script_loading.cpp index ea0c52e..7532bee 100644 --- a/src/client/component/gsc/script_loading.cpp +++ b/src/client/component/gsc/script_loading.cpp @@ -1,6 +1,7 @@ #include #include "loader/component_loader.hpp" #include "game/game.hpp" +#include "game/dvars.hpp" #include #include @@ -10,7 +11,6 @@ #include "component/filesystem.hpp" #include "component/console.hpp" #include "component/scripting.hpp" -#include "component/fastfiles.hpp" #include "script_loading.hpp" @@ -26,8 +26,6 @@ namespace gsc std::unordered_map loaded_scripts; utils::memory::allocator script_allocator; - const game::dvar_t* developer_script; - void clear() { main_handles.clear(); @@ -266,11 +264,19 @@ namespace gsc void scr_begin_load_scripts_stub() { - const auto comp_mode = developer_script->current.enabled ? - xsk::gsc::build::dev : - xsk::gsc::build::prod; + auto build = xsk::gsc::build::prod; - gsc_ctx->init(comp_mode, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair> + if ((*dvars::com_developer) && (*dvars::com_developer)->current.integer > 0) + { + build = static_cast(static_cast(build) | static_cast(xsk::gsc::build::dev_maps)); + } + + if (dvars::com_developer_script && dvars::com_developer_script->current.enabled) + { + build = static_cast(static_cast(build) | static_cast(xsk::gsc::build::dev_blocks)); + } + + gsc_ctx->init(build, []([[maybe_unused]] auto const* ctx, const auto& included_path) -> std::pair> { const auto script_name = std::filesystem::path(included_path).replace_extension().string(); @@ -331,13 +337,15 @@ namespace gsc void post_unpack() override { + dvars::com_developer = reinterpret_cast(SELECT_VALUE(0x141603850, 0x1419A9700)); + // Load our scripts with an uncompressed stack utils::hook::call(SELECT_VALUE(0x1403DC8F0, 0x140437940), db_get_raw_buffer_stub); utils::hook::call(SELECT_VALUE(0x14032D1E0, 0x1403CCED9), scr_begin_load_scripts_stub); // GScr_LoadScripts utils::hook::call(SELECT_VALUE(0x14032D345, 0x1403CD08D), scr_end_load_scripts_stub); // GScr_LoadScripts - developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments"); + dvars::com_developer_script = game::Dvar_RegisterBool("developer_script", false, game::DVAR_FLAG_NONE, "Enable developer script comments"); if (game::environment::is_sp()) { diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index e8d2436..0222a44 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -3,8 +3,9 @@ #include "game/game.hpp" #include "command.hpp" -#include "network.hpp" #include "console.hpp" +#include "network.hpp" +#include "party.hpp" #include #include @@ -87,14 +88,18 @@ namespace network return net_compare_base_address(a1, a2) && a1->port == a2->port; } - void reconnect_migratated_client(game::mp::client_t*, game::netadr_s* from, const int, const int, const char*, - const char*, bool) + void reconnect_migrated_client(game::mp::client_t*, game::netadr_s* from, const int, const int, const char*, const char*, bool) { // This happens when a client tries to rejoin after being recently disconnected, OR by a duplicated guid // We don't want this to do anything. It decides to crash seemingly randomly // Rather than try and let the player in, just tell them they are a duplicate player and reject connection game::NET_OutOfBandPrint(game::NS_SERVER, from, "error\nYou are already connected to the server."); } + + void* memmove_stub(void* dest, const void* src, std::size_t count) + { + return std::memmove(dest, src, std::min(count, 1262)); + } } void on(const std::string& command, const callback& callback) @@ -138,21 +143,21 @@ namespace network const char* net_adr_to_string(const game::netadr_s& a) { - if (a.type == game::netadrtype_t::NA_LOOPBACK) + if (a.type == game::NA_LOOPBACK) { return "loopback"; } - if (a.type == game::netadrtype_t::NA_BOT) + if (a.type == game::NA_BOT) { return "bot"; } - if (a.type == game::netadrtype_t::NA_IP || a.type == game::netadrtype_t::NA_BROADCAST) + if (a.type == game::NA_IP || a.type == game::NA_BROADCAST) { if (a.port) { - return utils::string::va("%u.%u.%u.%u:%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3], htons(a.port)); + return utils::string::va("%u.%u.%u.%u:%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ::htons(a.port)); } return utils::string::va("%u.%u.%u.%u", a.ip[0], a.ip[1], a.ip[2], a.ip[3]); @@ -189,10 +194,9 @@ namespace network a.jmp(0x14041DFBD); } - game::dvar_t* register_netport_stub(const char* dvarName, int value, int min, int max, unsigned int flags, - const char* description) + 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, std::numeric_limits::max(), game::DVAR_FLAG_LATCHED, "Network port"); // read net_port from command line command::read_startup_variable("net_port"); @@ -221,13 +225,13 @@ namespace network utils::hook::jump(0x14041DFB0, utils::hook::assemble(set_xuid_config_string_stub), true); utils::hook::jump(0x14041D010, net_compare_address); - utils::hook::jump(0x14041D060, net_compare_base_address); + utils::hook::jump(0x14041D060, net_compare_address); // don't establish secure conenction utils::hook::set(0x1402ECF1D, 0xEB); utils::hook::set(0x1402ED02A, 0xEB); utils::hook::set(0x1402ED34D, 0xEB); - utils::hook::set(0x1402C4A1F, 0xEB); // + utils::hook::set(0x1402C4A1F, 0xEB); // ignore unregistered connection utils::hook::jump(0x140471AAC, reinterpret_cast(0x140471A50)); @@ -272,17 +276,31 @@ namespace network utils::hook::jump(0x1405019CB, 0x1405019F3); // don't try to reconnect client - utils::hook::call(0x14047197E, reconnect_migratated_client); + utils::hook::call(0x14047197E, reconnect_migrated_client); // allow server owner to modify net_port before the socket bind utils::hook::call(0x140500FD0, register_netport_stub); - // ignore built in "print" oob command and add in our own + // ignore built in "print" oob command for security reasons utils::hook::set(0x1402C6AA4, 0xEB); - on("print", [](const game::netadr_s&, const std::string& data) + if (!game::environment::is_dedi()) { - console::info("%s", data.data()); - }); + // we need this on the client for RCon + on("print", [](const game::netadr_s& address, const std::string& message) + { + if (address != party::get_target()) + { + return; + } + + console::info("%s", message.data()); + }); + } + + // patch buffer overflow + utils::hook::call(0x14041D17E, memmove_stub); // NET_DeferPacketToClient + // this patches a crash found in a subroutine registered using atexit + utils::hook::set(0x140815D4E, 0xEB); } } }; diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index ccf635c..59afda4 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -60,54 +60,6 @@ namespace patches return com_register_dvars_hook.invoke(); } - utils::hook::detour dvar_register_int_hook; - - game::dvar_t* dvar_register_int(const char* name, int value, const int min, const int max, - const unsigned int flags, - const char* description) - { - // enable map selection in extinction - if (!strcmp(name, "extinction_map_selection_enabled")) - { - value = true; - } - - // enable extra loadouts - else if (!strcmp(name, "extendedLoadoutsEnable")) - { - value = true; - } - - // show all in-game store items - else if (strstr(name, "igs_")) - { - value = true; - } - - return dvar_register_int_hook.invoke(name, value, min, max, flags, description); - } - - game::dvar_t* register_fovscale_stub(const char* name, float /*value*/, float /*min*/, float /*max*/, - unsigned int /*flags*/, - const char* desc) - { - // changed max value from 2.0f -> 5.0f and min value from 0.5f -> 0.1f - return game::Dvar_RegisterFloat(name, 1.0f, 0.1f, 5.0f, game::DvarFlags::DVAR_FLAG_SAVED, desc); - } - - game::dvar_t* register_cg_gun_dvars(const char* name, float /*value*/, float /*min*/, float /*max*/, - unsigned int /*flags*/, const char* desc) - { - if (name == "cg_gun_x"s) - { - return game::Dvar_RegisterFloat(name, 0.0f, -1.0f, 2.0f, game::DvarFlags::DVAR_FLAG_SAVED, desc); - } - else - { - return game::Dvar_RegisterFloat(name, 0.0f, 0.0f, 0.0f, game::DvarFlags::DVAR_FLAG_NONE, desc); - } - } - game::dvar_t* register_network_fps_stub(const char* name, int, int, int, unsigned int flags, const char* desc) { @@ -180,14 +132,14 @@ namespace patches { if (args.size() == 1) { - const auto current = game::Dvar_ValueToString(dvar, dvar->current); - const auto reset = game::Dvar_ValueToString(dvar, dvar->reset); - console::info("\"%s\" is: \"%s^7\" default: \"%s^7\"\n", dvar->name, current, reset); + const std::string current = game::Dvar_ValueToString(dvar, dvar->current); + const std::string reset = game::Dvar_ValueToString(dvar, dvar->reset); + console::info("\"%s\" is: \"%s^7\" default: \"%s^7\"\n", dvar->name, current.data(), reset.data()); console::info(" %s\n", dvars::dvar_get_domain(dvar->type, dvar->domain).data()); } else { - char command[0x1000] = {0}; + char command[0x1000]{}; game::Dvar_GetCombinedString(command, 1); game::Dvar_SetCommand(args.get(0), command); } @@ -280,7 +232,7 @@ namespace patches LoadLibraryA("PhysXUpdateLoader64.dll"); // Unlock fps in main menu - utils::hook::set(SELECT_VALUE(0x140242DDB, 0x1402CF58B), 0xEB); + utils::hook::set(SELECT_VALUE(0x140242DDB, 0x1402CF58B), 0xEB); // Unlock cg_fov utils::hook::call(SELECT_VALUE(0x1401F3E96, 0x14027273C), register_cg_fov_stub); @@ -289,14 +241,10 @@ namespace patches utils::hook::call(0x1401F3EC7, register_cg_fov_stub); } - // set it to 3 to display both voice dlc announcers did only show 1 - game::Dvar_RegisterInt("igs_announcer", 3, 3, 3, game::DvarFlags::DVAR_FLAG_NONE, - "Show Announcer Packs. (Bitfield representing which announcer paks to show)"); - // changed max value from 85 -> 1000 if (!game::environment::is_dedi()) { - game::Dvar_RegisterInt("com_maxfps", 85, 0, 1000, game::DvarFlags::DVAR_FLAG_SAVED, "Cap frames per second"); + dvars::override::register_int("com_maxfps", 85, 0, 1000, game::DVAR_FLAG_SAVED); } if (!game::environment::is_sp()) @@ -307,12 +255,12 @@ namespace patches // register cg_gun_ dvars with new values and flags // maybe _x can stay usable within a reasonable range? it can make scoped weapons DRASTICALLY better on high FOVs - utils::hook::call(SELECT_VALUE(0x140228DDE, 0x1402AB04C), register_cg_gun_dvars); - utils::hook::call(SELECT_VALUE(0x140228E0E, 0x1402AB07C), register_cg_gun_dvars); - utils::hook::call(SELECT_VALUE(0x140228E3E, 0x1402AB0AC), register_cg_gun_dvars); + dvars::override::register_float("cg_gun_x", 0.0f, -1.0f, 2.0f, game::DVAR_FLAG_SAVED); + dvars::override::register_float("cg_gun_y", 0.0f, 0.0f, 0.0f, game::DVAR_FLAG_NONE); + dvars::override::register_float("cg_gun_z", 0.0f, 0.0f, 0.0f, game::DVAR_FLAG_NONE); // Register cg_fovscale with new params - utils::hook::call(SELECT_VALUE(0x140317079, 0x140272777), register_fovscale_stub); + dvars::override::register_float("cg_fovScale", 1.0f, 0.1f, 5.0f, game::DVAR_FLAG_SAVED); // Patch Dvar_Command to print out values how CoD4 does it utils::hook::jump(SELECT_VALUE(0x1403BFCB0, 0x140416A60), dvar_command_patch); @@ -320,8 +268,7 @@ namespace patches // Allow executing custom cfg files with the "exec" command utils::hook::jump(SELECT_VALUE(0x1403B39BB, 0x1403F752B), SELECT_VALUE(0x1403B3A12, 0x1403F7582)); //Use a relative jump to empty memory first - utils::hook::jump(SELECT_VALUE(0x1403B3A12, 0x1403F7582), SELECT_VALUE(cmd_exec_stub_sp, cmd_exec_stub_mp), - true); + utils::hook::jump(SELECT_VALUE(0x1403B3A12, 0x1403F7582), SELECT_VALUE(cmd_exec_stub_sp, cmd_exec_stub_mp), true); // Use empty memory to go to our stub first (can't do close jump, so need space for 12 bytes) // Fix mouse lag @@ -331,10 +278,6 @@ namespace patches SetThreadExecutionState(ES_DISPLAY_REQUIRED); }, scheduler::pipeline::main); - // Allow kbam input when gamepad is enabled - utils::hook::nop(SELECT_VALUE(0x14023D490, 0x1402C3099), 2); - utils::hook::nop(SELECT_VALUE(0x14023B3AC, 0x1402C0CE0), 6); - if (game::environment::is_sp()) { patch_sp(); @@ -365,7 +308,23 @@ namespace patches utils::hook::set(0x140599890, 0xC301B0); // Entitlements_IsIDUnlocked // Enable DLC items, extra loadouts and map selection in extinction - dvar_register_int_hook.create(0x1404EE270, &dvar_register_int); + dvars::override::register_int("extinction_map_selection_enabled", 1, 0, 1, 0); + dvars::override::register_int("extendedLoadoutsEnable", 1, 0, 1, 0); + dvars::override::register_int("igs_announcer", 3, 3, 3, 0); + dvars::override::register_int("igs_swp", 1, 0, 1, 0); + dvars::override::register_int("igs_shp", 1, 0, 1, 0); + dvars::override::register_int("igs_svp", 1, 0, 1, 0); + dvars::override::register_int("igs_sve", 1, 0, 1, 0); + dvars::override::register_int("igs_svs", 1, 0, 1, 0); + dvars::override::register_int("igs_svr", 1, 0, 1, 0); + dvars::override::register_int("igs_swap", 1, 0, 1, 0); + dvars::override::register_int("igs_fo", 1, 0, 1, 0); + dvars::override::register_int("igs_td", 1, 0, 1, 0); + dvars::override::register_int("igs_sripper", 1, 0, 1, 0); + dvars::override::register_int("igs_smappacks", 1, 0, 1, 0); + dvars::override::register_int("igs_sosp", 1, 0, 1, 0); + dvars::override::register_int("igs_s1", 1, 0, 1, 0); + dvars::override::register_int("igs_crossgame", 1, 0, 1, 0); // Patch game chat on resolutions higher than 1080p to use the right font utils::hook::call(0x14025C825, get_chat_font_handle); @@ -373,7 +332,7 @@ namespace patches utils::hook::call(0x1402C3699, get_chat_font_handle); dvars::aimassist_enabled = game::Dvar_RegisterBool("aimassist_enabled", true, - game::DvarFlags::DVAR_FLAG_SAVED, + game::DVAR_FLAG_SAVED, "Enables aim assist for controllers"); // Client side aim assist dvar utils::hook::call(0x14013B9AC, aim_assist_add_to_target_list); diff --git a/src/client/component/ranked.cpp b/src/client/component/ranked.cpp index 4ca1017..55bc4b4 100644 --- a/src/client/component/ranked.cpp +++ b/src/client/component/ranked.cpp @@ -17,10 +17,20 @@ namespace ranked utils::hook::detour bg_bot_fast_file_enabled_hook; utils::hook::detour bg_bots_using_team_difficulty_hook; - int bg_bot_system_enabled_stub() + bool should_activate_bot_system() { const auto* game_type = game::Dvar_FindVar("g_gametype")->current.string; if (!std::strcmp(game_type, "aliens") || !std::strcmp(game_type, "horde")) + { + return false; + } + + return true; + } + + int bg_bot_system_enabled_stub() + { + if (!should_activate_bot_system()) { return bg_bot_system_enabled_hook.invoke(); } @@ -30,8 +40,7 @@ namespace ranked int bg_ai_system_enabled_stub() { - const auto* game_type = game::Dvar_FindVar("g_gametype")->current.string; - if (!std::strcmp(game_type, "aliens") || !std::strcmp(game_type, "horde")) + if (!should_activate_bot_system()) { return bg_ai_system_enabled_hook.invoke(); } @@ -41,8 +50,7 @@ namespace ranked int bg_bot_fast_file_enabled_stub() { - const auto* game_type = game::Dvar_FindVar("g_gametype")->current.string; - if (!std::strcmp(game_type, "aliens") || !std::strcmp(game_type, "horde")) + if (!should_activate_bot_system()) { return bg_bot_fast_file_enabled_hook.invoke(); } @@ -52,8 +60,7 @@ namespace ranked int bg_bots_using_team_difficulty_stub() { - const auto* game_type = game::Dvar_FindVar("g_gametype")->current.string; - if (!std::strcmp(game_type, "aliens") || !std::strcmp(game_type, "horde")) + if (!should_activate_bot_system()) { return bg_bots_using_team_difficulty_hook.invoke(); } @@ -86,7 +93,7 @@ namespace ranked game::Dvar_RegisterBool("force_ranking", true, game::DVAR_FLAG_WRITE, "Force ranking"); // Fix sessionteam always returning none (SV_HasAssignedTeam_Internal) - utils::hook::set(0x140479CF0, 0xC300B0); + utils::hook::set(0x140479CF0, 0xC300B0); } // Always run bots, even if xblive_privatematch is 0 diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp index d6a9d65..52dff19 100644 --- a/src/client/component/rcon.cpp +++ b/src/client/component/rcon.cpp @@ -15,6 +15,7 @@ namespace rcon { bool is_redirecting_ = false; game::netadr_s redirect_target_ = {}; + std::string redirect_buffer = {}; std::recursive_mutex redirect_lock; void setup_redirect(const game::netadr_s& target) @@ -23,14 +24,18 @@ namespace rcon is_redirecting_ = true; redirect_target_ = target; + redirect_buffer.clear(); } void clear_redirect() { std::lock_guard $(redirect_lock); + network::send(redirect_target_, "print", redirect_buffer, '\n'); + is_redirecting_ = false; redirect_target_ = {}; + redirect_buffer.clear(); } std::string build_status_buffer() @@ -48,7 +53,7 @@ namespace rcon const auto client = &game::mp::svs_clients[i]; auto self = &game::mp::g_entities[i]; - char clean_name[32] = {0}; + char clean_name[32]{}; strncpy_s(clean_name, self->client->sess.cs.name, sizeof(clean_name)); game::I_CleanStr(clean_name); @@ -104,9 +109,10 @@ namespace rcon if (is_redirecting_) { - network::send(redirect_target_, "print\n", message); + redirect_buffer.append(message); return true; } + return false; } @@ -175,7 +181,7 @@ namespace rcon const auto password = data.substr(0, pos); const auto command = data.substr(pos + 1); - const auto rcon_password = game::Dvar_FindVar("rcon_password"); + const auto* rcon_password = game::Dvar_FindVar("rcon_password"); if (command.empty() || !rcon_password || !*rcon_password->current.string) { return; diff --git a/src/client/game/demonware/services/bdDML.cpp b/src/client/game/demonware/services/bdDML.cpp index 8bc7fc0..2777f2b 100644 --- a/src/client/game/demonware/services/bdDML.cpp +++ b/src/client/game/demonware/services/bdDML.cpp @@ -13,7 +13,7 @@ namespace demonware { auto result = new bdDMLRawData; result->country_code = "US"; - result->country_code = "'Murica"; + result->country = "United States of America"; result->region = "New York"; result->city = "New York"; result->latitude = 0; diff --git a/src/client/game/dvars.cpp b/src/client/game/dvars.cpp index 04c0e96..63b4eb5 100644 --- a/src/client/game/dvars.cpp +++ b/src/client/game/dvars.cpp @@ -45,6 +45,9 @@ namespace dvars game::dvar_t* cg_legacyCrashHandling = nullptr; + game::dvar_t* com_developer_script = nullptr; + game::dvar_t** com_developer; + std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain) { if (domain.vector.min == -FLT_MAX) @@ -78,14 +81,14 @@ namespace dvars switch (type) { case game::dvar_type::boolean: - return "Domain is 0 or 1"s; + return "Domain is 0 or 1"; case game::dvar_type::value: if (domain.value.min == -FLT_MAX) { if (domain.value.max == FLT_MAX) { - return "Domain is any number"s; + return "Domain is any number"; } return utils::string::va("Domain is any number %g or smaller", domain.value.max); @@ -113,7 +116,7 @@ namespace dvars { if (domain.integer.max == INT_MAX) { - return "Domain is any integer"s; + return "Domain is any integer"; } return utils::string::va("Domain is any integer %i or smaller", domain.integer.max); @@ -127,10 +130,10 @@ namespace dvars return utils::string::va("Domain is any integer from %i to %i", domain.integer.min, domain.integer.max); case game::dvar_type::color: - return "Domain is any 4-component color, in RGBA format"s; + return "Domain is any 4-component color, in RGBA format"; case game::dvar_type::enumeration: - str = "Domain is one of the following:"s; + str = "Domain is one of the following:"; for (auto string_index = 0; string_index < domain.enumeration.stringCount; ++string_index) { @@ -140,7 +143,7 @@ namespace dvars return str; case game::dvar_type::string: - return "Domain is any text"s; + return "Domain is any text"; default: return utils::string::va("unhandled dvar type '%i'", type); diff --git a/src/client/game/dvars.hpp b/src/client/game/dvars.hpp index 88d465b..c273a6b 100644 --- a/src/client/game/dvars.hpp +++ b/src/client/game/dvars.hpp @@ -44,6 +44,9 @@ namespace dvars extern game::dvar_t* cg_legacyCrashHandling; + extern game::dvar_t* com_developer_script; + extern game::dvar_t** com_developer; + 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/main.cpp b/src/client/main.cpp index 83514e4..e50f1cc 100644 --- a/src/client/main.cpp +++ b/src/client/main.cpp @@ -12,6 +12,21 @@ #include +const char* get_current_date() +{ + auto now = std::chrono::system_clock::now(); + auto current_time = std::chrono::system_clock::to_time_t(now); + std::tm local_time{}; + + (void)localtime_s(&local_time, ¤t_time); + + std::stringstream ss; + ss << std::put_time(&local_time, "%Y%m%d_%H%M%S"); + + const auto result = ss.str(); + return utils::string::va("%s", result.data()); +} + LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) { if (exception_info->ExceptionRecord->ExceptionCode == 0x406D1388) @@ -38,7 +53,7 @@ LONG WINAPI exception_handler(PEXCEPTION_POINTERS exception_info) | MiniDumpWithThreadInfo; CreateDirectoryA("minidumps", nullptr); - const auto* file_name = utils::string::va("minidumps\\iw6-mod_%s_%u.dmp", SHORTVERSION, static_cast(std::time(nullptr))); + const auto* file_name = utils::string::va("minidumps\\iw6-mod_%s_%s.dmp", SHORTVERSION, get_current_date()); constexpr auto file_share = FILE_SHARE_READ | FILE_SHARE_WRITE; const auto file_handle = CreateFileA(file_name, GENERIC_WRITE | GENERIC_READ, file_share, nullptr,