From 7de1ffbe9d04c6f5090bb42002aa924116ddad44 Mon Sep 17 00:00:00 2001 From: FutureRave Date: Sat, 13 May 2023 10:25:14 +0100 Subject: [PATCH] fix: hot join join issue for dedis & dangling pointer in script --- src/client/component/bots.cpp | 7 +-- src/client/component/colors.cpp | 2 - src/client/component/dedicated_patches.cpp | 3 +- src/client/component/getinfo.cpp | 10 +--- src/client/component/party.cpp | 2 +- src/client/component/patches.cpp | 53 ++++++++++++++-------- src/client/component/scheduler.cpp | 8 ++-- src/client/component/scheduler.hpp | 8 ++-- src/client/component/script.cpp | 21 ++++----- src/client/game/structs.hpp | 24 ++++++++-- src/client/game/symbols.cpp | 5 -- src/client/game/symbols.hpp | 16 +++++-- src/client/game/utils.cpp | 6 +-- 13 files changed, 94 insertions(+), 71 deletions(-) diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 56d39d01..43f124ae 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -94,9 +94,9 @@ namespace bots } int format_bot_string(char* buffer, [[maybe_unused]] const char* format, const char* name, const char* xuid, - const char* xnaddr, int protocol, int net_field_chk, const char* session_mode, int qport) + const char* xnaddr, int protocol, int net_field_chk, const char* session_mode, int qport) { - const auto find_name = [](const std::string& needle) -> const char* + const auto find_clan_name = [](const std::string& needle) -> const char* { for (const auto& entry : get_bot_names()) { @@ -109,7 +109,8 @@ namespace bots return "3arc"; }; - return sprintf_s(buffer, 1024, bot_format_string, name, find_name(name), xuid, xnaddr, protocol, net_field_chk, session_mode, qport); + return sprintf_s(buffer, 1024, bot_format_string, name, find_clan_name(name), + xuid, xnaddr, protocol, net_field_chk, session_mode, qport); } } diff --git a/src/client/component/colors.cpp b/src/client/component/colors.cpp index 5c7f7b4e..3ffe2c41 100644 --- a/src/client/component/colors.cpp +++ b/src/client/component/colors.cpp @@ -5,8 +5,6 @@ #include "auth.hpp" -#include "steam/steam.hpp" - #include #include diff --git a/src/client/component/dedicated_patches.cpp b/src/client/component/dedicated_patches.cpp index 80a0245c..11f3862d 100644 --- a/src/client/component/dedicated_patches.cpp +++ b/src/client/component/dedicated_patches.cpp @@ -2,7 +2,6 @@ #include "loader/component_loader.hpp" #include "game/game.hpp" -#include "scheduler.hpp" #include @@ -12,7 +11,7 @@ namespace dedicated_patches { utils::hook::detour spawn_server_hook; - void scr_are_textures_loaded_stub([[maybe_unused]] game::scriptInstance_t inst) + void scr_are_textures_loaded_stub() { game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, 1); } diff --git a/src/client/component/getinfo.cpp b/src/client/component/getinfo.cpp index b40c902a..5efcf49b 100644 --- a/src/client/component/getinfo.cpp +++ b/src/client/component/getinfo.cpp @@ -68,14 +68,6 @@ namespace getinfo return count; } - namespace - { - int Com_SessionMode_GetGameMode() - { - return *reinterpret_cast(game::select(0x1568ED7F4, 0x14948DB04)) << 14 >> 28; - } - } - int get_assigned_team() { return (rand() % 2) + 1; @@ -112,7 +104,7 @@ namespace getinfo info.set("protocol", std::to_string(PROTOCOL)); info.set("sub_protocol", std::to_string(SUB_PROTOCOL)); info.set("playmode", std::to_string(game::Com_SessionMode_GetMode())); - info.set("gamemode", std::to_string(Com_SessionMode_GetGameMode())); + info.set("gamemode", std::to_string(game::Com_SessionMode_GetGameMode())); info.set("sv_running", std::to_string(game::is_server_running())); info.set("dedicated", game::is_server() ? "1" : "0"); info.set("hc", std::to_string(game::Com_GametypeSettings_GetUInt("hardcoremode", false))); diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 0ee400d5..07a15f11 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -54,7 +54,7 @@ namespace party { const auto local_client = *reinterpret_cast(0x14342155C_g); const auto current_mode = game::Com_SessionMode_GetMode(); - game::Com_SwitchMode(local_client, current_mode, mode, 6); + game::Com_SwitchMode(local_client, static_cast(current_mode), mode, 6); }, scheduler::main); } diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 662ccf97..e1794351 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -2,8 +2,7 @@ #include "loader/component_loader.hpp" #include - -#include "network.hpp" +#include #include @@ -11,21 +10,10 @@ namespace patches { namespace { - utils::hook::detour sv_execute_client_messages_hook; + const game::dvar_t* lobby_min_players; - void sv_execute_client_messages_stub(game::client_s* client, game::msg_t* msg) - { - if ((client->reliableSequence - 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, ...) + void script_errors_stub([[maybe_unused]] const char* file, [[maybe_unused]] int line, + [[maybe_unused]] unsigned int code, const char* fmt, ...) { char buffer[0x1000]; @@ -38,6 +26,30 @@ namespace patches game::Com_Error(game::ERROR_SCRIPT_DROP, "%s", buffer); } + + void scr_get_num_expected_players() + { + const auto mode = game::Com_SessionMode_GetMode(); + if (mode == game::MODE_ZOMBIES || mode == game::MODE_CAMPAIGN) + { + game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, lobby_min_players->current.value.integer); + } + + const auto num_expected_players = std::max(1, game::LobbyHost_GetClientCount(game::LOBBY_TYPE_GAME, game::LOBBY_CLIENT_TYPE_ALL)); + game::Scr_AddInt(game::SCRIPTINSTANCE_SERVER, num_expected_players); + } + + void sv_execute_client_messages_stub(game::client_s* client, game::msg_t* msg) + { + if ((client->reliableSequence - client->reliableAcknowledge) < 0) + { + client->reliableAcknowledge = client->reliableSequence; + game::SV_DropClient(client, "EXE_LOSTRELIABLECOMMANDS", true, true); + return; + } + + game::SV_ExecuteClientMessage(client, msg); + } } struct component final : generic_component @@ -53,13 +65,16 @@ namespace patches // 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 + // 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); + // make sure reliableAck is not negative or too big + utils::hook::call(game::select(0x14225489C, 0x140537C4C), sv_execute_client_messages_stub); + + lobby_min_players = game::register_dvar_int("lobby_min_players", 1, 1, 8, game::DVAR_NONE, ""); + utils::hook::jump(game::select(0x141A7BCF0, 0x1402CB900), scr_get_num_expected_players, true); } }; } diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp index a115b1d9..7cbeb0b9 100644 --- a/src/client/component/scheduler.cpp +++ b/src/client/component/scheduler.cpp @@ -84,7 +84,7 @@ namespace scheduler }; volatile bool kill = false; - std::thread thread; + std::thread async_thread; task_pipeline pipelines[pipeline::count]; utils::hook::detour r_end_frame_hook; @@ -153,7 +153,7 @@ namespace scheduler { void post_load() override { - thread = utils::thread::create_named_thread("Async Scheduler", []() + async_thread = utils::thread::create_named_thread("Async Scheduler", []() { while (!kill) { @@ -180,9 +180,9 @@ namespace scheduler void pre_destroy() override { kill = true; - if (thread.joinable()) + if (async_thread.joinable()) { - thread.join(); + async_thread.join(); } } }; diff --git a/src/client/component/scheduler.hpp b/src/client/component/scheduler.hpp index 8949b6e4..56aaf8a0 100644 --- a/src/client/component/scheduler.hpp +++ b/src/client/component/scheduler.hpp @@ -30,12 +30,12 @@ namespace scheduler void execute(const pipeline type); - void schedule(const std::function& callback, pipeline type = pipeline::async, + void schedule(const std::function& callback, pipeline type, std::chrono::milliseconds delay = 0ms); - void loop(const std::function& callback, pipeline type = pipeline::async, + void loop(const std::function& callback, pipeline type, std::chrono::milliseconds delay = 0ms); - void once(const std::function& callback, pipeline type = pipeline::async, + void once(const std::function& callback, pipeline type, std::chrono::milliseconds delay = 0ms); - void on_game_initialized(const std::function& callback, pipeline type = pipeline::async, + void on_game_initialized(const std::function& callback, pipeline type, std::chrono::milliseconds delay = 0ms); } diff --git a/src/client/component/script.cpp b/src/client/component/script.cpp index 2958dae6..0016d087 100644 --- a/src/client/component/script.cpp +++ b/src/client/component/script.cpp @@ -14,7 +14,7 @@ namespace script { constexpr size_t GSC_MAGIC = 0x1C000A0D43534780; - utils::hook::detour db_findxassetheader_hook; + utils::hook::detour db_find_x_asset_header_hook; utils::hook::detour gscr_get_bgb_remaining_hook; std::unordered_map loaded_scripts; @@ -33,7 +33,6 @@ namespace script void load_script(std::string& name, const std::string& data) { auto& allocator = *utils::memory::get_allocator(); - const auto* file_string = allocator.duplicate_string(data); const auto appdata_path = (game::get_appdata_path() / "data/").generic_string(); const auto host_path = (utils::nt::library{}.get_folder() / "boiii/").generic_string(); @@ -50,12 +49,12 @@ namespace script name.erase(i, host_path.length()); } - auto* rawfile = allocator.allocate(); - rawfile->name = name.c_str(); - rawfile->buffer = file_string; - rawfile->len = static_cast(data.length()); + auto* raw_file = allocator.allocate(); + raw_file->name = allocator.duplicate_string(name); + raw_file->buffer = allocator.duplicate_string(data); + raw_file->len = static_cast(data.length()); - loaded_scripts[name] = rawfile; + loaded_scripts[name] = raw_file; } void load_scripts_folder(const std::string& script_dir) @@ -94,11 +93,11 @@ namespace script load_scripts_folder((host.get_folder() / "boiii/scripts").string()); } - game::RawFile* db_findxassetheader_stub(const game::XAssetType type, const char* name, + game::RawFile* db_find_x_asset_header_stub(const game::XAssetType type, const char* name, const bool error_if_missing, const int wait_time) { - auto* asset_header = db_findxassetheader_hook.invoke( + auto* asset_header = db_find_x_asset_header_hook.invoke( type, name, error_if_missing, wait_time); if (type != game::ASSET_TYPE_SCRIPTPARSETREE) @@ -134,10 +133,10 @@ namespace script } else { - scheduler::once(load_scripts, scheduler::pipeline::renderer); + scheduler::once(load_scripts, scheduler::pipeline::main); } - db_findxassetheader_hook.create(game::select(0x141420ED0, 0x1401D5FB0), db_findxassetheader_stub); + db_find_x_asset_header_hook.create(game::select(0x141420ED0, 0x1401D5FB0), db_find_x_asset_header_stub); gscr_get_bgb_remaining_hook.create(game::select(0x141A8CAB0, 0x1402D2310), gscr_get_bgb_remaining_stub); } }; diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index bc861685..1ceca9a2 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -890,7 +890,7 @@ namespace game enum LobbyType { - LOBBY_TYPE_INVALID = 0xFFFFFFFF, + LOBBY_TYPE_INVALID = -1, LOBBY_TYPE_PRIVATE = 0x0, LOBBY_TYPE_GAME = 0x1, LOBBY_TYPE_TRANSITION = 0x2, @@ -900,6 +900,14 @@ namespace game LOBBY_TYPE_AUTO = 0x3, }; + enum LobbyClientType + { + LOBBY_CLIENT_TYPE_INVALID = -1, + LOBBY_CLIENT_TYPE_ALL = 0x0, + LOBBY_CLIENT_TYPE_LOCAL = 0x1, + LOBBY_CLIENT_TYPE_REMOTE = 0x2, + }; + enum LobbyNetworkMode { LOBBY_NETWORKMODE_INVALID = 0xFFFFFFFF, @@ -1600,7 +1608,6 @@ namespace game char __pad6[171432]; }; - #ifdef __cplusplus static_assert(sizeof(client_s) == 0xE5110); @@ -1637,14 +1644,23 @@ namespace game struct EntityState { int number; - }; + }; // Incomplete struct gentity_s { EntityState s; unsigned char __pad0[0x24C]; gclient_s* client; - unsigned char __pad1[0x2A0]; + unsigned char __pad1[0x17C]; + struct + { + unsigned int notifyString; + unsigned int index; + unsigned char stoppable; + int basetime; + int duration; + } snd_wait; + unsigned char __pad2[0x110]; }; #ifdef __cplusplus diff --git a/src/client/game/symbols.cpp b/src/client/game/symbols.cpp index fb36ce71..789acbb7 100644 --- a/src/client/game/symbols.cpp +++ b/src/client/game/symbols.cpp @@ -4,11 +4,6 @@ namespace game { - eModes Com_SessionMode_GetMode() - { - return eModes(*reinterpret_cast(game::select(0x1568ED7F4, 0x14948DB04)) << 28 >> 28); - } - bool I_islower(int c) { return c >= 'a' && c <= 'z'; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 470af2aa..9df36d84 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -25,6 +25,8 @@ namespace game WEAK symbol Com_Printf{0x142148F60, 0x140505630}; WEAK symbol Com_Error_{0x1420F8170, 0x140501470}; WEAK symbol Com_SessionMode_IsMode{0x1420F7370}; + WEAK symbol Com_SessionMode_GetMode{0x1420F6D30 , 0x1405002D0}; + WEAK symbol Com_SessionMode_GetGameMode{0x1420F68B0, 0x1404FFE50}; WEAK symbol Com_SessionMode_SetNetworkMode{0x1420F75B0, 0x140500B80}; WEAK symbol Com_SessionMode_SetGameMode{0x1420F7570, 0x140500B40}; WEAK symbol Com_SessionMode_SetMode{0x1420F7570}; @@ -131,7 +133,7 @@ namespace game }; WEAK symbol Dvar_RegisterInt{ - 0x0, 0x14057B7B0 + 0x1422D0AE0, 0x14057B7B0 }; WEAK symbol Dvar_RegisterFloat{ @@ -204,10 +206,20 @@ namespace game }; WEAK symbol SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0}; WEAK symbol SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670}; + WEAK symbol SV_ExecuteClientMessage{0x14224A460, 0x14052F840}; + + WEAK symbol SV_DropClient{ + 0x14224A050, 0x14052F430 + }; // FS WEAK symbol FS_AllocMem{0x1422AC9F0, 0x14056C340}; + // Lobby + WEAK symbol LobbyHost_GetClientCount{ + 0x141ED8AC0, 0x14048A360 + }; + // Utils WEAK symbol I_CleanStr{0x1422E9050, 0x140580E80}; WEAK symbol I_strcpy{ @@ -261,8 +273,6 @@ namespace game constexpr auto CMD_MAX_NESTING = 8; // Re-implementations - eModes Com_SessionMode_GetMode(); - bool I_islower(int c); bool I_isupper(int c); diff --git a/src/client/game/utils.cpp b/src/client/game/utils.cpp index 4f6fb99a..cc083632 100644 --- a/src/client/game/utils.cpp +++ b/src/client/game/utils.cpp @@ -136,11 +136,10 @@ namespace game } auto* dvar_to_change = dvar; - if (dvar_to_change->type == DVAR_TYPE_SESSIONMODE_BASE_DVAR) { const auto mode = Com_SessionMode_GetMode(); - dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, mode); + dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, static_cast(mode)); } dvar_to_change->flags |= flags; @@ -156,11 +155,10 @@ namespace game } auto* dvar_to_change = dvar; - if (dvar_to_change->type == DVAR_TYPE_SESSIONMODE_BASE_DVAR) { const auto mode = Com_SessionMode_GetMode(); - dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, mode); + dvar_to_change = Dvar_GetSessionModeSpecificDvar(dvar_to_change, static_cast(mode)); } dvar_to_change->flags = flags;