diff --git a/data/scripts/mp/gametypes/_serversettings.gsc b/data/scripts/mp/gametypes/_serversettings.gsc index cd63bd0b..bf3985aa 100644 Binary files a/data/scripts/mp/gametypes/_serversettings.gsc and b/data/scripts/mp/gametypes/_serversettings.gsc differ diff --git a/data/scripts/mp/gametypes/_serversettings.gsc_raw b/data/scripts/mp/gametypes/_serversettings.gsc_raw index 02669e86..e420ca3c 100644 --- a/data/scripts/mp/gametypes/_serversettings.gsc_raw +++ b/data/scripts/mp/gametypes/_serversettings.gsc_raw @@ -3,11 +3,9 @@ #using scripts\shared\callbacks_shared; #using scripts\shared\system_shared; -#insert scripts\shared\shared.gsh; - #namespace serversettings; -REGISTER_SYSTEM( "serversettings", &__init__, undefined ) +function autoexec __init__sytem__() { system::register("serversettings",&__init__,undefined,undefined); } function __init__() { @@ -21,26 +19,29 @@ function init() level.hostname = "CoDHost"; SetDvar("sv_hostname", level.hostname); SetDvar("ui_hostname", level.hostname); + //makeDvarServerInfo("ui_hostname", "CoDHost"); level.motd = GetDvarString( "scr_motd" ); if(level.motd == "") level.motd = ""; SetDvar("scr_motd", level.motd); SetDvar("ui_motd", level.motd); + //makeDvarServerInfo("ui_motd", ""); - level.allowvote = GetDvarString( "g_allowvote"); + level.allowvote = GetDvarString( "g_allowvote" ); if(level.allowvote == "") level.allowvote = "1"; SetDvar("g_allowvote", level.allowvote); SetDvar("ui_allowvote", level.allowvote); + //makeDvarServerInfo("ui_allowvote", "1"); level.allow_teamchange = "1"; SetDvar("ui_allow_teamchange", level.allow_teamchange); - level.friendlyfire = GetGametypeSetting( "friendlyfiretype" ); SetDvar("ui_friendlyfire", level.friendlyfire); + //makeDvarServerInfo("ui_friendlyfire", "0"); if(GetDvarString( "scr_mapsize") == "") SetDvar("scr_mapsize", "64"); @@ -56,6 +57,8 @@ function init() constrain_gametype(GetDvarString( "g_gametype")); constrain_map_size(level.mapsize); + + thread setup_callbacks(); for(;;) { @@ -80,7 +83,7 @@ function update() SetDvar("ui_motd", level.motd); } - g_allowvote = GetDvarString( "g_allowvote"); + g_allowvote = GetDvarString( "g_allowvote" ); if(level.allowvote != g_allowvote) { level.allowvote = g_allowvote; @@ -193,4 +196,14 @@ function constrain_map_size(mapsize) } } } +} + +function setup_callbacks() +{ + level.onForfeit = &default_onForfeit; +} + +function default_onForfeit() +{ + level.gameForfeited = false; } \ No newline at end of file diff --git a/data/ui_scripts/stats/__init__.lua b/data/ui_scripts/stats/__init__.lua index 5ac66426..70a1eeac 100644 --- a/data/ui_scripts/stats/__init__.lua +++ b/data/ui_scripts/stats/__init__.lua @@ -20,7 +20,7 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup("MPStatsSettings", fun table.insert(optionsTable, CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Loot", - "Whether loot should be locked based on the player's stats or always unlocked.", "MPStatsSettings_unlock_loot", + "Unlocks all Black Market loot.", "MPStatsSettings_unlock_loot", "cg_unlockall_loot", { { option = "MENU_DISABLED", @@ -47,6 +47,20 @@ DataSources.MPStatsSettings = DataSourceHelpers.ListSetup("MPStatsSettings", fun value = 1 }, }, nil, updateDvar)) + table.insert(optionsTable, + CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Class Slots", + "Unlocks all create-a-class slots and sets.", "MPStatsSettings_unlockall_cac_slots", + "cg_unlockall_cac_slots", { + { + option = "MENU_DISABLED", + value = 0, + default = true + }, + { + option = "MENU_ENABLED", + value = 1 + }, + }, nil, updateDvar)) end table.insert(optionsTable, CoD.OptionsUtility.CreateDvarSettings(controller, "Unlock All Attachments", diff --git a/deps/curl b/deps/curl index dc18b40b..1c5ed24e 160000 --- a/deps/curl +++ b/deps/curl @@ -1 +1 @@ -Subproject commit dc18b40b406e9946a2198d66b6edb62575660faf +Subproject commit 1c5ed24ee0e929a6f410fcc3729becfd2ee71211 diff --git a/deps/zlib b/deps/zlib index eb0e038b..66588683 160000 --- a/deps/zlib +++ b/deps/zlib @@ -1 +1 @@ -Subproject commit eb0e038b297f2c9877ed8b3515c6718a4b65d485 +Subproject commit 66588683b36042154ad35140bf9fcbb60c5d573c diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index 57346b8f..1457ee8e 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -2,6 +2,7 @@ #include "loader/component_loader.hpp" #include "auth.hpp" +#include "party.hpp" #include "command.hpp" #include "network.hpp" #include "scheduler.hpp" @@ -24,6 +25,8 @@ namespace auth { namespace { + std::array client_xuids{}; + std::string get_hdd_serial() { DWORD serial{}; @@ -107,8 +110,6 @@ namespace auth buffer.write_string(data, static_cast(length)); - printf("Serialized with size: %llX\n", buffer.get_buffer().size()); - return buffer.move_buffer(); } @@ -153,6 +154,59 @@ namespace auth return 0; } + void distribute_player_xuid(const game::netadr_t& target, const size_t player_index, const uint64_t xuid) + { + if (player_index >= 18) + { + return; + } + + utils::byte_buffer buffer{}; + buffer.write(static_cast(player_index)); + buffer.write(xuid); + + game::foreach_connected_client([&](const game::client_s& client, const size_t index) + { + if (client.address.type != game::NA_BOT) + { + network::send(client.address, "playerXuid", buffer.get_buffer()); + } + + if (index != player_index && target.type != game::NA_BOT) + { + utils::byte_buffer current_buffer{}; + current_buffer.write(static_cast(index)); + current_buffer.write(client.xuid); + + network::send(target, "playerXuid", current_buffer.get_buffer()); + } + }); + } + + void handle_new_player(const game::netadr_t& target) + { + const command::params_sv params{}; + if (params.size() < 2) + { + return; + } + + const utils::info_string info_string(params[1]); + const auto xuid = strtoull(info_string.get("xuid").data(), nullptr, 16); + + size_t player_index = 18; + game::foreach_connected_client([&](game::client_s& client, const size_t index) + { + if (client.address == target) + { + client.xuid = xuid; + player_index = index; + } + }); + + distribute_player_xuid(target, player_index, xuid); + } + void dispatch_connect_packet(const game::netadr_t& target, const std::string& data) { utils::byte_buffer buffer(data); @@ -174,14 +228,7 @@ namespace auth profile_infos::add_and_distribute_profile_info(target, xuid, info); game::SV_DirectConnect(target); - - game::foreach_connected_client([&](game::client_s& client) - { - if (client.address == target) - { - client.xuid = xuid; - } - }); + handle_new_player(target); } void handle_connect_packet_fragment(const game::netadr_t& target, const network::data_view& data) @@ -196,9 +243,36 @@ namespace auth std::string final_packet{}; if (game::fragment_handler::handle(target, buffer, final_packet)) { - dispatch_connect_packet(target, final_packet); + scheduler::once([t = target, p = std::move(final_packet)] + { + dispatch_connect_packet(t, p); + }, scheduler::server); } } + + void handle_player_xuid_packet(const game::netadr_t& target, const network::data_view& data) + { + if (game::is_server_running() || !party::is_host(target)) + { + return; + } + + utils::byte_buffer buffer(data); + + const auto player_id = buffer.read(); + const auto xuid = buffer.read(); + + if (player_id < client_xuids.size()) + { + client_xuids[player_id] = xuid; + } + } + + void direct_connect_bots_stub(const game::netadr_t address) + { + game::SV_DirectConnect(address); + handle_new_player(address); + } } uint64_t get_guid() @@ -216,6 +290,40 @@ namespace auth return guid; } + uint64_t get_guid(const size_t client_num) + { + if (client_num >= 18) + { + return 0; + } + + if (!game::is_server_running()) + { + return client_xuids[client_num]; + } + + uint64_t xuid = 0; + const auto callback = [&xuid](const game::client_s& client) + { + xuid = client.xuid; + }; + + if (!game::access_connected_client(client_num, callback)) + { + return 0; + } + + return xuid; + } + + void clear_stored_guids() + { + for (auto& xuid : client_xuids) + { + xuid = 0; + } + } + struct component final : generic_component { void post_unpack() override @@ -223,6 +331,10 @@ namespace auth // Skip connect handler utils::hook::set(game::select(0x142253EFA, 0x14053714A), 0xEB); network::on("connect", handle_connect_packet_fragment); + network::on("playerXuid", handle_player_xuid_packet); + + // Intercept SV_DirectConnect in SV_AddTestClient + utils::hook::call(game::select(0x1422490DC, 0x14052E582), direct_connect_bots_stub); // Patch steam id bit check std::vector> patches{}; diff --git a/src/client/component/auth.hpp b/src/client/component/auth.hpp index 3ee0c53a..30341c98 100644 --- a/src/client/component/auth.hpp +++ b/src/client/component/auth.hpp @@ -3,4 +3,6 @@ namespace auth { uint64_t get_guid(); + uint64_t get_guid(size_t client_num); + void clear_stored_guids(); } diff --git a/src/client/component/client_patches.cpp b/src/client/component/client_patches.cpp index 9d824961..f8a9a9b5 100644 --- a/src/client/component/client_patches.cpp +++ b/src/client/component/client_patches.cpp @@ -116,6 +116,9 @@ namespace client_patches { fix_amd_cpu_stuttering(); + // Don't modify process priority + utils::hook::nop(0x142334C98_g, 6); + // Kill microphones for now utils::hook::set(0x15AAE9254_g, mixer_open_stub); diff --git a/src/client/component/colors.cpp b/src/client/component/colors.cpp index 306c44b6..5c7f7b4e 100644 --- a/src/client/component/colors.cpp +++ b/src/client/component/colors.cpp @@ -3,6 +3,10 @@ #include "game/game.hpp" +#include "auth.hpp" + +#include "steam/steam.hpp" + #include #include @@ -10,8 +14,25 @@ namespace colors { namespace { - utils::hook::detour get_player_name_hook; - utils::hook::detour get_gamer_tag_hook; + utils::hook::detour cl_get_client_name_hook; + + std::optional get_color_for_xuid(const uint64_t xuid) + { + if (xuid == 0xCD02AF6448291209 + || xuid == 0x10F0C433E08E1357 + || xuid == 0x60E0FEFE42341715) + { + return 2; + } + + return {}; + } + + std::optional get_color_for_client(const int client_num) + { + const auto xuid = auth::get_guid(static_cast(client_num)); + return get_color_for_xuid(xuid); + } template void patch_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a = 255) @@ -36,23 +57,39 @@ namespace colors utils::hook::copy(g_color_table + index * 4, color_float, sizeof(color_float)); } - /*uint64_t get_player_name_stub(const uint64_t client, int client_num, char* buffer, const int size, - const bool has_clan_tag) + bool cl_get_client_name_stub(const int local_client_num, const int index, char* buf, const int size, + const bool add_clan_name) { - const auto res = get_player_name_hook.invoke(client, client_num, buffer, size, has_clan_tag); + const auto res = cl_get_client_name_hook.invoke(local_client_num, index, buf, size, add_clan_name); - if (_ReturnAddress() != reinterpret_cast(0x1406A7B56_g)) + if (_ReturnAddress() == reinterpret_cast(0x1406A7B56_g)) { - const auto val = utils::string::va("^%d%s", rand() % 7, buffer); - strncpy_s(buffer, size, val, size); + return res; } - return res; - }*/ + const auto color = get_color_for_client(index); + if (!color) + { + return res; + } - /*const char* get_gamer_tag_stub(const uint64_t num) + const auto val = utils::string::va("^%d%s", *color, buf); + utils::string::copy(buf, size, val); + + return res; + } + + /*const char* get_gamer_tag_stub(const uint32_t num) { - return utils::string::va("^3%s", get_gamer_tag_hook.invoke(num)); + const auto color = get_color_for_xuid(steam::SteamUser()->GetSteamID().bits); + const auto name = reinterpret_cast(0x141EC6E80)(num) + 8; + + if (!color || num) + { + return name; + } + + return utils::string::va("^1%s", *color, name); }*/ } @@ -68,8 +105,8 @@ namespace colors patch_color<6>(151, 80, 221); // 6 - Pink // Old addresses - //get_player_name_hook.create(0x1413E3140_g, get_player_name_stub); - //get_gamer_tag_hook.create(0x141EC7370_g, get_gamer_tag_stub); + cl_get_client_name_hook.create(game::CL_GetClientName, cl_get_client_name_stub); + //utils::hook::jump(0x141EC72E0_g, get_gamer_tag_stub); } }; } diff --git a/src/client/component/loot.cpp b/src/client/component/loot.cpp index 291bcdd6..39a54490 100644 --- a/src/client/component/loot.cpp +++ b/src/client/component/loot.cpp @@ -17,6 +17,7 @@ namespace loot const game::dvar_t* dvar_cg_unlockall_camos_and_reticles; const game::dvar_t* dvar_cg_unlockall_calling_cards; const game::dvar_t* dvar_cg_unlockall_specialists_outfits; + const game::dvar_t* dvar_cg_unlockall_cac_slots; utils::hook::detour loot_getitemquantity_hook; utils::hook::detour liveinventory_getitemquantity_hook; @@ -30,7 +31,8 @@ namespace loot utils::hook::detour bg_unlockablescharactercustomizationitemlocked_hook; utils::hook::detour bg_emblemisentitlementbackgroundgranted_hook; utils::hook::detour liveentitlements_isentitlementactiveforcontroller_hook; - + utils::hook::detour bg_unlockablesgetcustomclasscount_hook; + int loot_getitemquantity_stub(const game::ControllerIndex_t controller_index, const game::eModes mode, const int item_id) { if (!dvar_cg_unlockall_loot->current.value.enabled) @@ -48,19 +50,25 @@ namespace loot int liveinventory_getitemquantity_stub(const game::ControllerIndex_t controller_index, const int item_id) { - // Item id's for extra CaC slots, CWL camo's and paid specialist outfits + // Item id's for CWL camo's and paid specialist outfits if (dvar_cg_unlockall_loot->current.value.enabled && (item_id == 99003 || item_id >= 99018 && item_id <= 99021 || item_id == 99025 || item_id >= 90047 && item_id <= 90064)) { return 1; } + // Item id for extra CaC slots + if (dvar_cg_unlockall_cac_slots->current.value.enabled && item_id == 99003) + { + return 1; + } + return liveinventory_getitemquantity_hook.invoke(controller_index, item_id); } bool liveinventory_areextraslotspurchased_stub(const game::ControllerIndex_t controller_index) { - if (dvar_cg_unlockall_loot->current.value.enabled) + if (dvar_cg_unlockall_cac_slots->current.value.enabled) { return true; } @@ -160,9 +168,19 @@ namespace loot return liveentitlements_isentitlementactiveforcontroller_hook.invoke(controllerIndex, incentiveId); } + + int bg_unlockablesgetcustomclasscount_stub(game::eModes mode, const game::ControllerIndex_t controllerIndex) + { + if (dvar_cg_unlockall_cac_slots->current.value.enabled) + { + return 10; + } + + return bg_unlockablesgetcustomclasscount_hook.invoke(mode, controllerIndex); + } }; - struct component final : client_component + struct component final: client_component { void post_unpack() override { @@ -172,6 +190,7 @@ namespace loot dvar_cg_unlockall_camos_and_reticles = game::register_dvar_bool("cg_unlockall_camos_and_reticles", false, game::DVAR_ARCHIVE, "Unlocks all camos and reticles"); dvar_cg_unlockall_calling_cards = game::register_dvar_bool("cg_unlockall_calling_cards", false, game::DVAR_ARCHIVE, "Unlocks all calling cards"); dvar_cg_unlockall_specialists_outfits = game::register_dvar_bool("cg_unlockall_specialists_outfits", false, game::DVAR_ARCHIVE, "Unlocks all specialists outfits"); + dvar_cg_unlockall_cac_slots = game::register_dvar_bool("cg_unlockall_cac_slots", false, game::DVAR_ARCHIVE, "Unlocks all Create a Class Slots"); loot_getitemquantity_hook.create(0x141E82C00_g, loot_getitemquantity_stub); liveinventory_getitemquantity_hook.create(0x141E09030_g, liveinventory_getitemquantity_stub); @@ -185,6 +204,7 @@ namespace loot bg_unlockablescharactercustomizationitemlocked_hook.create(0x1426A2030_g, bg_unlockablescharactercustomizationitemlocked_stub); bg_emblemisentitlementbackgroundgranted_hook.create(0x142667520_g, bg_emblemisentitlementbackgroundgranted_stub); liveentitlements_isentitlementactiveforcontroller_hook.create(0x141E124E0_g, liveentitlements_isentitlementactiveforcontroller_stub); + bg_unlockablesgetcustomclasscount_hook.create(0x1426A5900_g, bg_unlockablesgetcustomclasscount_stub); scheduler::once([]() { if (dvar_cg_unlockall_loot->current.value.enabled) diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 546d5a1f..4371a17a 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -163,7 +163,6 @@ namespace network : 0; } - uint64_t ret2() { return 2; @@ -173,6 +172,15 @@ namespace network { return 0; } + + void com_error_oob_stub(const char* file, int line, int code, [[maybe_unused]] const char* fmt, const char* error) + { + char buffer[1024]{}; + + strncpy_s(buffer, error, _TRUNCATE); + + game::Com_Error_(file, line, code, "%s", buffer); + } } void on(const std::string& command, const callback& callback) @@ -321,10 +329,21 @@ namespace network // NA_IP -> NA_RAWIP in NetAdr_ToString utils::hook::set(game::select(0x142172ED4, 0x140515864), game::NA_RAWIP); + // Kill 'echo' OOB handler + utils::hook::set(game::select(0x14134D0FB, 0x14018EE82), 0xEB); + if (game::is_server()) { // Remove restrictions for rcon commands - utils::hook::call(0x140538D5C_g, &con_restricted_execute_buf_stub); // SVC_RemoteCommand + utils::hook::call(0x140538D5C_g, con_restricted_execute_buf_stub); // SVC_RemoteCommand + + // Kill 'error' OOB handler on the dedi + utils::hook::nop(0x14018EF8B_g, 5); + } + else + { + // Truncate error string to make sure there are no buffer overruns later + utils::hook::call(0x14134D206_g, com_error_oob_stub); } // TODO: Fix that diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index ad478876..55baa1ec 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -3,6 +3,7 @@ #include "game/game.hpp" #include "party.hpp" +#include "auth.hpp" #include "network.hpp" #include "scheduler.hpp" #include "workshop.hpp" @@ -39,6 +40,8 @@ namespace party void connect_to_lobby(const game::netadr_t& addr, const std::string& mapname, const std::string& gamemode, const std::string& usermap_id, const std::string& mod_id) { + auth::clear_stored_guids(); + workshop::load_mod_if_needed(usermap_id, mod_id); game::XSESSION_INFO info{}; @@ -56,7 +59,8 @@ namespace party } void connect_to_lobby_with_mode(const game::netadr_t& addr, const game::eModes mode, const std::string& mapname, - const std::string& gametype, const std::string& usermap_id, const std::string& mod_id, + const std::string& gametype, const std::string& usermap_id, + const std::string& mod_id, const bool was_retried = false) { if (game::Com_SessionMode_IsMode(mode)) @@ -189,7 +193,7 @@ namespace party const auto usermap_id = workshop::get_usermap_publisher_id(mapname); if (workshop::check_valid_usermap_id(mapname, usermap_id) && - workshop::check_valid_mod_id(mod_id)) + workshop::check_valid_mod_id(mod_id)) { if (is_connecting_to_dedi) { diff --git a/src/client/component/party.hpp b/src/client/component/party.hpp index f682a14e..b10ee95c 100644 --- a/src/client/component/party.hpp +++ b/src/client/component/party.hpp @@ -1,6 +1,8 @@ #pragma once #include +#include "game/game.hpp" + namespace party { using query_callback_func = void(bool success, const game::netadr_t& host, const ::utils::info_string& info, uint32_t ping); diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index bfec3e5c..b7a2824f 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -1,41 +1,68 @@ -#include -#include "loader/component_loader.hpp" - -#include -#include - -namespace patches -{ - namespace - { - void script_errors_stub(const char* file, int line, unsigned int code, const char* fmt, ...) - { - char buffer[0x1000]; - - { - va_list ap; - va_start(ap, fmt); - vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); - va_end(ap); - } - - game::Com_Error(game::ERROR_SCRIPT_DROP, "%s", buffer); - } - } - - struct component final : generic_component - { - void post_unpack() override - { - // don't make script errors fatal error - utils::hook::call(game::select(0x1412CAC4D, 0x140158EB2), script_errors_stub); - - // Change 4 character name limit to 3 characters - utils::hook::set(game::select(0x14224DA53, 0x140531143), 3); - utils::hook::set(game::select(0x14224DBB4, 0x1405312A8), 3); - utils::hook::set(game::select(0x14224DF8C, 0x1405316DC), 3); - } - }; -} - -REGISTER_COMPONENT(patches::component) +#include +#include "loader/component_loader.hpp" + +#include +#include + +#include "network.hpp" +#include "scheduler.hpp" + +#include + +namespace patches +{ + namespace + { + utils::hook::detour sv_execute_client_messages_hook; + + void sv_execute_client_messages_stub(game::client_s* client, game::msg_t* msg) + { + if (client->reliableAcknowledge < 0) + { + client->reliableAcknowledge = client->reliableSequence; + network::send(client->address, "error", "EXE_LOSTRELIABLECOMMANDS"); + return; + } + + sv_execute_client_messages_hook.invoke(client, msg); + } + + void script_errors_stub(const char* file, int line, unsigned int code, const char* fmt, ...) + { + char buffer[0x1000]; + + { + va_list ap; + va_start(ap, fmt); + vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt, ap); + va_end(ap); + } + + game::Com_Error(game::ERROR_SCRIPT_DROP, "%s", buffer); + } + } + + struct component final : generic_component + { + void post_unpack() override + { + // don't make script errors fatal error + utils::hook::call(game::select(0x1412CAC4D, 0x140158EB2), script_errors_stub); + + // Change 4 character name limit to 3 characters + utils::hook::set(game::select(0x14224DA53, 0x140531143), 3); + utils::hook::set(game::select(0x14224DBB4, 0x1405312A8), 3); + utils::hook::set(game::select(0x14224DF8C, 0x1405316DC), 3); + + // make sure client's reliableAck are not negative + sv_execute_client_messages_hook.create(game::select(0x14224A460, 0x14052F840), sv_execute_client_messages_stub); + + scheduler::once([] + { + game::register_dvar_string("password", "", game::DVAR_USERINFO, "password"); + }, scheduler::pipeline::main); + } + }; +} + +REGISTER_COMPONENT(patches::component) diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp index d4ac94b8..a115b1d9 100644 --- a/src/client/component/scheduler.cpp +++ b/src/client/component/scheduler.cpp @@ -88,7 +88,6 @@ namespace scheduler task_pipeline pipelines[pipeline::count]; utils::hook::detour r_end_frame_hook; - utils::hook::detour g_run_frame_hook; utils::hook::detour main_frame_hook; @@ -98,9 +97,9 @@ namespace scheduler r_end_frame_hook.invoke(); } - void server_frame_stub() + void g_clear_vehicle_inputs_stub() { - g_run_frame_hook.invoke(); + game::G_ClearVehicleInputs(); execute(pipeline::server); } @@ -168,12 +167,14 @@ namespace scheduler { if (!game::is_server()) { - r_end_frame_hook.create(0x142272B00_g, r_end_frame_stub); // some func called before R_EndFrame, maybe SND_EndFrame? + r_end_frame_hook.create(0x142272B00_g, r_end_frame_stub); } - main_frame_hook.create(game::select(0x1420F8E00, 0x1405020E0), main_frame_stub); // Com_Frame_Try_Block_Function + main_frame_hook.create(game::select(0x1420F8E00, 0x1405020E0), main_frame_stub); + + utils::hook::call(game::select(0x14225522E, 0x140538427), g_clear_vehicle_inputs_stub); } void pre_destroy() override diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 424c59ac..9876f2c4 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1583,15 +1583,23 @@ namespace game int client_state; char __pad0[0x28]; netadr_t address; - char __pad1[0x5588]; + char __pad1[20468]; + int reliableSequence; + int reliableAcknowledge; + char __pad2[4]; + int messageAcknowledge; + char gap_5040[1416]; uint64_t xuid; - char __pad2[0xB5D84]; + char __pad3[0xB5D84]; int guid; - char __pad3[0x8]; + char __pad4[0x8]; bool bIsTestClient; - char __pad4[0x29DAC]; + char __pad5[3]; + int serverId; + char __pad6[171432]; }; + #ifdef __cplusplus static_assert(sizeof(client_s) == 0xE5110); diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 9942c54c..c938ac18 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -75,6 +75,11 @@ namespace game WEAK symbol DB_FileExists{0x141420B40}; WEAK symbol DB_ReleaseXAssets{0x1414247C0}; + // G + WEAK symbol G_ClearVehicleInputs{0x1423812E0, 0x1405C1200}; + + WEAK symbol StuckInClient{0x1415A8360, 0x14023BFE0}; + // Live WEAK symbol Live_GetConnectivityInformation{0x141E0C380}; diff --git a/src/client/game/utils.cpp b/src/client/game/utils.cpp index e374a73a..c61a4350 100644 --- a/src/client/game/utils.cpp +++ b/src/client/game/utils.cpp @@ -176,6 +176,30 @@ namespace game } } + + template + static bool access_client(T* client_states, const size_t index, const std::function& callback) + { + if (!client_states || !callback) + { + return false; + } + + if (index >= get_max_client_count()) + { + return false; + } + + auto& client = client_states[index]; + if (client.client_state <= 0) + { + return false; + } + + callback(client); + return true; + } + void foreach_client(const std::function& callback) { if (is_server()) @@ -214,4 +238,14 @@ namespace game callback(client); }); } + + bool access_connected_client(const size_t index, const std::function& callback) + { + if (is_server()) + { + return access_client(*svs_clients, index, callback); + } + + return access_client(*svs_clients_cl, index, callback); + } } diff --git a/src/client/game/utils.hpp b/src/client/game/utils.hpp index 09abd8e7..5bbc78c0 100644 --- a/src/client/game/utils.hpp +++ b/src/client/game/utils.hpp @@ -24,4 +24,6 @@ namespace game void foreach_connected_client(const std::function& callback); void foreach_connected_client(const std::function& callback); + + bool access_connected_client(size_t index, const std::function& callback); } diff --git a/src/client/std_include.hpp b/src/client/std_include.hpp index 59b83a41..422c5ed9 100644 --- a/src/client/std_include.hpp +++ b/src/client/std_include.hpp @@ -65,6 +65,7 @@ #endif #include +#include #include #include #include diff --git a/src/client/updater/file_updater.cpp b/src/client/updater/file_updater.cpp index bee3cf86..f68b6d2d 100644 --- a/src/client/updater/file_updater.cpp +++ b/src/client/updater/file_updater.cpp @@ -200,7 +200,11 @@ namespace updater throw; } - utils::nt::relaunch_self(); + if (!utils::flags::has_flag("norelaunch")) + { + utils::nt::relaunch_self(); + } + throw update_cancelled(); } diff --git a/src/common/utils/io.cpp b/src/common/utils/io.cpp index e981315d..6efce973 100644 --- a/src/common/utils/io.cpp +++ b/src/common/utils/io.cpp @@ -194,18 +194,19 @@ namespace utils::io std::vector list_files(const std::filesystem::path& directory, const bool recursive) { + std::error_code code{}; std::vector files; if (recursive) { - for (auto& file : std::filesystem::recursive_directory_iterator(directory)) + for (auto& file : std::filesystem::recursive_directory_iterator(directory, code)) { files.push_back(file.path()); } } else { - for (auto& file : std::filesystem::directory_iterator(directory)) + for (auto& file : std::filesystem::directory_iterator(directory, code)) { files.push_back(file.path()); }