diff --git a/README.md b/README.md index d5129478..dfa32513 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,7 @@

-An experimental client for Call of Duty Modern Warfare Remastered (version `1.4.814118.0-0.32767`). -Research for the latest Steam version of the game is on the [1.15](https://github.com/h1-mod/h1-mod/tree/1.15) branch. +An client for Call of Duty: Modern Warfare Remastered (version `1.15.1251288.0-0.32767`). [This project is based on S1x.](https://github.com/XLabsProject/s1x-client) diff --git a/data/ui_scripts/hud_info/hud.lua b/data/ui_scripts/hud_info/hud.lua index ffc8dc07..15f59b5b 100644 --- a/data/ui_scripts/hud_info/hud.lua +++ b/data/ui_scripts/hud_info/hud.lua @@ -93,6 +93,7 @@ function infoelement(data) local value = LUI.UIText.new({ left = left + 5, top = textoffsety, + font = labelfont.Font, height = textheight, leftAnchor = true, topAnchor = true, @@ -156,3 +157,4 @@ LUI.onmenuopen("mp_hud", function(hud) hud.static:addElement(infobar) end) + diff --git a/data/ui_scripts/mods/loading.lua b/data/ui_scripts/mods/loading.lua index 16cc3084..9600d8a5 100644 --- a/data/ui_scripts/mods/loading.lua +++ b/data/ui_scripts/mods/loading.lua @@ -2,15 +2,13 @@ game:addlocalizedstring("MENU_MODS", "MODS") game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.") game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3") -game:addlocalizedstring("LUA_MENU_OPEN_STORE", "Open store") -game:addlocalizedstring("LUA_MENU_OPEN_STORE_DESC", "Download and install mods.") game:addlocalizedstring("LUA_MENU_LOADED_MOD", "Loaded mod: ^2&&1") game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods") game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload") game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.") function createdivider(menu, text) - local element = LUI.UIElement.new( { + local element = LUI.UIElement.new({ leftAnchor = true, rightAnchor = true, left = 0, @@ -26,7 +24,10 @@ function createdivider(menu, text) title_bar_text = Engine.ToUpperCase(text) })) + element.text = element:getFirstChild():getFirstChild():getNextSibling() + menu.list:addElement(element) + return element end function string:truncate(length) @@ -48,12 +49,17 @@ LUI.addmenubutton("main_campaign", { function getmodname(path) local name = path - local desc = Engine.Localize("@LUA_MENU_MOD_DESC_DEFAULT", name) + game:addlocalizedstring(name, name) + game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.") + local desc = Engine.Localize("LUA_MENU_MOD_DESC_DEFAULT", name) local infofile = path .. "/info.json" if (io.fileexists(infofile)) then pcall(function() local data = json.decode(io.readfile(infofile)) + game:addlocalizedstring(data.description, data.description) + game:addlocalizedstring(data.author, data.author) + game:addlocalizedstring(data.version, data.version) desc = Engine.Localize("@LUA_MENU_MOD_DESC", data.description, data.author, data.version) name = data.name @@ -63,7 +69,7 @@ function getmodname(path) return name, desc end -LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1) +LUI.MenuBuilder.registerType("mods_menu", function(a1) local menu = LUI.MenuTemplate.new(a1, { menu_title = "@MENU_MODS", exclusiveController = 0, @@ -74,7 +80,8 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1) local modfolder = game:getloadedmod() if (modfolder ~= "") then - createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", getmodname(modfolder):truncate(24))) + local name = getmodname(modfolder) + createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", name:truncate(24))) menu:AddButton("@LUA_MENU_UNLOAD", function() Engine.Exec("unloadmod") @@ -113,4 +120,4 @@ LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1) menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu) return menu -end +end) diff --git a/data/ui_scripts/server_list/lobby.lua b/data/ui_scripts/server_list/lobby.lua index 9d31f6cf..93e01a90 100644 --- a/data/ui_scripts/server_list/lobby.lua +++ b/data/ui_scripts/server_list/lobby.lua @@ -27,13 +27,13 @@ function menu_xboxlive(f16_arg0, f16_arg1) menu:AddCACButton() menu:AddBarracksButton() menu:AddPersonalizationButton() - -- menu:AddDepotButton() + menu:AddDepotButton() end - serverListButton = menu:AddButton("@MENU_PRIVATE_MATCH", MPLobbyOnline.OnPrivateMatch, + local privateMatchButton = menu:AddButton("@MENU_PRIVATE_MATCH", MPLobbyOnline.OnPrivateMatch, MPLobbyOnline.disablePrivateMatchButton) - serverListButton:rename("menu_xboxlive_private_match") - serverListButton:setDisabledRefreshRate(500) + privateMatchButton:rename("menu_xboxlive_private_match") + privateMatchButton:setDisabledRefreshRate(500) if not Engine.IsCoreMode() then local leaderboardButton = menu:AddButton("@LUA_MENU_LEADERBOARD", "OpLeaderboardMain") leaderboardButton:rename("OperatorMenu_leaderboard") diff --git a/data/ui_scripts/stats/__init__.lua b/data/ui_scripts/stats/__init__.lua index 9975bfe5..306c7ae0 100644 --- a/data/ui_scripts/stats/__init__.lua +++ b/data/ui_scripts/stats/__init__.lua @@ -1,5 +1,5 @@ if (game:issingleplayer() or not Engine.InFrontend()) then - return + return end game:addlocalizedstring("LUA_MENU_STATS", "Stats") @@ -7,11 +7,15 @@ game:addlocalizedstring("LUA_MENU_STATS_DESC", "Edit player stats settings.") game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS", "Unlock all items") game:addlocalizedstring("LUA_MENU_UNLOCKALL_ITEMS_DESC", - "Whether items should be locked based on the player's stats or always unlocked.") + "Whether items should be locked based on the player's stats or always unlocked.") + +game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT", "Unlock all loot") +game:addlocalizedstring("LUA_MENU_UNLOCKALL_LOOT_DESC", + "Whether loot should be locked based on the player's stats or always unlocked.") game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES", "Unlock all classes") game:addlocalizedstring("LUA_MENU_UNLOCKALL_CLASSES_DESC", - "Whether classes should be locked based on the player's stats or always unlocked.") + "Whether classes should be locked based on the player's stats or always unlocked.") game:addlocalizedstring("LUA_MENU_PRESTIGE", "Prestige") game:addlocalizedstring("LUA_MENU_PRESTIGE_DESC", "Edit prestige level.") @@ -25,166 +29,143 @@ game:addlocalizedstring("LUA_MENU_SETTINGS", "Settings") game:addlocalizedstring("LUA_MENU_EDIT_STATS", "Edit Stats") function createdivider(menu, text) - local element = LUI.UIElement.new({ - leftAnchor = true, - rightAnchor = true, - left = 0, - right = 0, - topAnchor = true, - bottomAnchor = false, - top = 0, - bottom = 33.33 - }) + local element = LUI.UIElement.new({ + leftAnchor = true, + rightAnchor = true, + left = 0, + right = 0, + topAnchor = true, + bottomAnchor = false, + top = 0, + bottom = 33.33 + }) - element.scrollingToNext = true - element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", { - title_bar_text = Engine.ToUpperCase(Engine.Localize(text)) - })) + element.scrollingToNext = true + element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", { + title_bar_text = Engine.ToUpperCase(Engine.Localize(text)) + })) - menu.list:addElement(element) + menu.list:addElement(element) end local personalizationbutton = LUI.MPLobbyBase.AddPersonalizationButton LUI.MPLobbyBase.AddPersonalizationButton = function(menu) - personalizationbutton(menu) - menu:AddButton("@LUA_MENU_STATS", function() - LUI.FlowManager.RequestAddMenu(nil, "stats_menu") - end) + personalizationbutton(menu) + menu:AddButton("@LUA_MENU_STATS", function() + LUI.FlowManager.RequestAddMenu(nil, "stats_menu") + end) end LUI.MenuBuilder.registerType("stats_menu", function(a1) - local menu = LUI.MenuTemplate.new(a1, { - menu_title = Engine.ToUpperCase(Engine.Localize("@LUA_MENU_STATS")), - menu_width = luiglobals.GenericMenuDims.OptionMenuWidth - }) + local menu = LUI.MenuTemplate.new(a1, { + menu_title = Engine.ToUpperCase(Engine.Localize("@LUA_MENU_STATS")), + menu_width = luiglobals.GenericMenuDims.OptionMenuWidth + }) - createdivider(menu, "@LUA_MENU_SETTINGS") + createdivider(menu, "@LUA_MENU_SETTINGS") - LUI.Options.CreateOptionButton(menu, "cg_unlockall_items", "@LUA_MENU_UNLOCKALL_ITEMS", - "@LUA_MENU_UNLOCKALL_ITEMS_DESC", {{ - text = "@LUA_MENU_ENABLED", - value = true - }, { - text = "@LUA_MENU_DISABLED", - value = false - }}, nil, nil) + LUI.Options.CreateOptionButton(menu, "cg_unlockall_items", "@LUA_MENU_UNLOCKALL_ITEMS", + "@LUA_MENU_UNLOCKALL_ITEMS_DESC", {{ + text = "@LUA_MENU_ENABLED", + value = true + }, { + text = "@LUA_MENU_DISABLED", + value = false + }}, nil, nil) - LUI.Options.CreateOptionButton(menu, "cg_unlockall_classes", "@LUA_MENU_UNLOCKALL_CLASSES", - "@LUA_MENU_UNLOCKALL_CLASSES_DESC", {{ - text = "@LUA_MENU_ENABLED", - value = true - }, { - text = "@LUA_MENU_DISABLED", - value = false - }}, nil, nil) + LUI.Options.CreateOptionButton(menu, "cg_unlockall_loot", "@LUA_MENU_UNLOCKALL_LOOT", + "@LUA_MENU_UNLOCKALL_LOOT_DESC", {{ + text = "@LUA_MENU_ENABLED", + value = true + }, { + text = "@LUA_MENU_DISABLED", + value = false + }}, nil, nil) - createdivider(menu, "@LUA_MENU_EDIT_STATS") + LUI.Options.CreateOptionButton(menu, "cg_unlockall_classes", "@LUA_MENU_UNLOCKALL_CLASSES", + "@LUA_MENU_UNLOCKALL_CLASSES_DESC", {{ + text = "@LUA_MENU_ENABLED", + value = true + }, { + text = "@LUA_MENU_DISABLED", + value = false + }}, nil, nil) - local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 - local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0 - local rank = luiglobals.Lobby.GetRankForXP(experience, prestige) + createdivider(menu, "@LUA_MENU_EDIT_STATS") - local saved = true - local prestigevalue = prestige - local rankvalue = rank - local rankbutton = nil + local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 + local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0 + local rank = Lobby.GetRankForXP(experience, prestige) - prestigeeditbutton(menu, function(value) - prestigevalue = value - saved = false - end) + prestigeeditbutton(menu, function(value) + Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "prestige", tonumber(value)) + end) - rankbutton = rankeditbutton(menu, function(value) - rankvalue = value - saved = false - end) + rankeditbutton(menu, function(value) + local rank = tonumber(value) + local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 + local experience = rank == 0 and 0 or Rank.GetRankMaxXP(tonumber(value) - 1, prestige) - local savebutton = menu:AddButton("@LUA_MENU_SAVE", function() - Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "prestige", tonumber(prestigevalue)) + Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "experience", experience) + end) - local rank = tonumber(rankvalue) - local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 - local experience = rank == 0 and 0 or luiglobals.Rank.GetRankMaxXP(tonumber(rankvalue) - 1, prestige) + LUI.Options.InitScrollingList(menu.list, nil) + LUI.Options.AddOptionTextInfo(menu) - Engine.SetPlayerData(0, CoD.StatsGroup.Ranked, "experience", experience) + menu:AddBackButton() - saved = true - end, nil, nil, nil, { - desc_text = Engine.Localize("LUA_MENU_SAVE_DESC") - }) - - LUI.Options.InitScrollingList(menu.list, nil) - LUI.Options.AddOptionTextInfo(menu) - - menu:AddBackButton(function() - if (saved) then - LUI.FlowManager.RequestLeaveMenu(menu) - return - end - - LUI.yesnopopup({ - title = Engine.Localize("@MENU_NOTICE"), - text = Engine.Localize("@LUA_MENU_UNSAVED_CHANGES"), - callback = function(result) - if (result) then - LUI.FlowManager.RequestLeaveMenu(menu) - end - end - }) - end) - - return menu + return menu end) function prestigeeditbutton(menu, callback) - local options = {} - local max = luiglobals.Lobby.GetMaxPrestigeLevel() - local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 + local options = {} + local max = Lobby.GetMaxPrestigeLevel() + local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 - for i = 0, max do - game:addlocalizedstring("LUA_MENU_" .. i, i .. "") + for i = 0, max do + game:addlocalizedstring("LUA_MENU_" .. i, i .. "") - table.insert(options, { - text = "@" .. i, - value = i .. "" - }) - end + table.insert(options, { + text = "@" .. i, + value = i .. "" + }) + end - Engine.SetDvarFromString("ui_prestige_level", prestige .. "") + Engine.SetDvarFromString("ui_prestige_level", prestige .. "") - LUI.Options.CreateOptionButton(menu, "ui_prestige_level", "@LUA_MENU_PRESTIGE", "@LUA_MENU_PRESTIGE_DESC", options, - nil, nil, callback) + LUI.Options.CreateOptionButton(menu, "ui_prestige_level", "@LUA_MENU_PRESTIGE", "@LUA_MENU_PRESTIGE_DESC", options, + nil, nil, callback) end function rankeditbutton(menu, callback) - local options = {} - local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 - local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0 + local options = {} + local prestige = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "prestige") or 0 + local experience = Engine.GetPlayerData(0, CoD.StatsGroup.Ranked, "experience") or 0 - local rank = luiglobals.Lobby.GetRankForXP(experience, prestige) - local max = luiglobals.Rank.GetMaxRank(prestige) - local maxprestige = luiglobals.Lobby.GetMaxPrestigeLevel() + local rank = Lobby.GetRankForXP(experience, prestige) + local max = Rank.GetMaxRank(prestige) + local maxprestige = Lobby.GetMaxPrestigeLevel() - for i = 0, max do - game:addlocalizedstring("LUA_MENU_" .. i, i .. "") + for i = 0, max do + game:addlocalizedstring("LUA_MENU_" .. i, i .. "") - table.insert(options, { - text = "@" .. (i + 1), - value = i .. "" - }) - end + table.insert(options, { + text = "@" .. (i + 1), + value = i .. "" + }) + end - Engine.SetDvarFromString("ui_rank_level_", rank .. "") + Engine.SetDvarFromString("ui_rank_level_", rank .. "") - return LUI.Options.CreateOptionButton(menu, "ui_rank_level_", "@LUA_MENU_RANK", "@LUA_MENU_RANK_DESC", options, nil, - nil, callback) + return LUI.Options.CreateOptionButton(menu, "ui_rank_level_", "@LUA_MENU_RANK", "@LUA_MENU_RANK_DESC", options, nil, + nil, callback) end -local isclasslocked = luiglobals.Cac.IsCustomClassLocked -luiglobals.Cac.IsCustomClassLocked = function(...) - if (Engine.GetDvarBool("cg_unlockall_classes")) then - return false - end +local isclasslocked = Cac.IsCustomClassLocked +Cac.IsCustomClassLocked = function(...) + if (Engine.GetDvarBool("cg_unlockall_classes")) then + return false + end - return isclasslocked(table.unpack({...})) + return isclasslocked(...) end diff --git a/premake5.lua b/premake5.lua index fc9e6e5b..7bb114b7 100644 --- a/premake5.lua +++ b/premake5.lua @@ -303,7 +303,7 @@ targetname "h1-mod" pchheader "std_include.hpp" pchsource "src/client/std_include.cpp" -linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/LAST:.main", "/PDBCompress"} +linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/PDBCompress"} files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"} diff --git a/src/client/component/arxan.cpp b/src/client/component/arxan.cpp new file mode 100644 index 00000000..3f77095f --- /dev/null +++ b/src/client/component/arxan.cpp @@ -0,0 +1,145 @@ +#include +#include "loader/component_loader.hpp" +#include "scheduler.hpp" +#include "game/game.hpp" + +#include + +namespace arxan +{ + namespace + { + utils::hook::detour nt_close_hook; + utils::hook::detour nt_query_information_process_hook; + + NTSTATUS WINAPI nt_query_information_process_stub(const HANDLE handle, const PROCESSINFOCLASS info_class, + const PVOID info, + const ULONG info_length, const PULONG ret_length) + { + auto* orig = static_cast(nt_query_information_process_hook. + get_original()); + const auto status = orig(handle, info_class, info, info_length, ret_length); + + if (NT_SUCCESS(status)) + { + if (info_class == ProcessBasicInformation) + { + static DWORD explorer_pid = 0; + if (!explorer_pid) + { + auto* const shell_window = GetShellWindow(); + GetWindowThreadProcessId(shell_window, &explorer_pid); + } + + static_cast(info)->Reserved3 = PVOID(DWORD64(explorer_pid)); + } + else if (info_class == 30) // ProcessDebugObjectHandle + { + *static_cast(info) = nullptr; + + return 0xC0000353; + } + else if (info_class == 7) // ProcessDebugPort + { + *static_cast(info) = nullptr; + } + else if (info_class == 31) + { + *static_cast(info) = 1; + } + + //https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess + } + + return status; + } + + NTSTATUS NTAPI nt_close_stub(const HANDLE handle) + { + char info[16]; + if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS(4), &info, 2, nullptr) >= 0 && size_t(handle) != 0x12345) + { + auto* orig = static_cast(nt_close_hook.get_original()); + return orig(handle); + } + + return STATUS_INVALID_HANDLE; + } + + LONG WINAPI exception_filter(const LPEXCEPTION_POINTERS info) + { + if (info->ExceptionRecord->ExceptionCode == STATUS_INVALID_HANDLE) + { + return EXCEPTION_CONTINUE_EXECUTION; + } + + return EXCEPTION_CONTINUE_SEARCH; + } + + void hide_being_debugged() + { + auto* const peb = PPEB(__readgsqword(0x60)); + peb->BeingDebugged = false; + *reinterpret_cast(LPSTR(peb) + 0xBC) &= ~0x70; + } + + void remove_hardware_breakpoints() + { + CONTEXT context; + ZeroMemory(&context, sizeof(context)); + context.ContextFlags = CONTEXT_DEBUG_REGISTERS; + + auto* const thread = GetCurrentThread(); + GetThreadContext(thread, &context); + + context.Dr0 = 0; + context.Dr1 = 0; + context.Dr2 = 0; + context.Dr3 = 0; + context.Dr6 = 0; + context.Dr7 = 0; + + SetThreadContext(thread, &context); + } + + BOOL WINAPI set_thread_context_stub(const HANDLE thread, CONTEXT* context) + { + return SetThreadContext(thread, context); + } + } + + class component final : public component_interface + { + public: + void* load_import(const std::string& library, const std::string& function) override + { + if (function == "SetThreadContext") + { + //return set_thread_context_stub; + } + + return nullptr; + } + + void post_load() override + { + hide_being_debugged(); + scheduler::loop(hide_being_debugged, scheduler::pipeline::async); + + const utils::nt::library ntdll("ntdll.dll"); + nt_close_hook.create(ntdll.get_proc("NtClose"), nt_close_stub); + nt_query_information_process_hook.create(ntdll.get_proc("NtQueryInformationProcess"), + nt_query_information_process_stub); + // https://www.geoffchappell.com/studies/windows/win32/ntdll/api/index.htm + AddVectoredExceptionHandler(1, exception_filter); + } + + void post_unpack() override + { + // cba to implement sp, not sure if it's even needed + if (game::environment::is_sp()) return; + } + }; +} + +REGISTER_COMPONENT(arxan::component) \ No newline at end of file diff --git a/src/client/component/auth.cpp b/src/client/component/auth.cpp index f0950e96..21ede068 100644 --- a/src/client/component/auth.cpp +++ b/src/client/component/auth.cpp @@ -89,7 +89,7 @@ namespace auth return utils::string::va("0x%lX", value); } - int send_connect_data_stub(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len) + bool send_connect_data(game::netsrc_t sock, game::netadr_s* adr, const char* format, const int len) { std::string connect_string(format, len); game::SV_Cmd_TokenizeString(connect_string.data()); @@ -189,7 +189,29 @@ namespace auth a.call_aligned(direct_connect); a.popad64(); - a.jmp(0x140488CE2); // H1MP64(1.4) + a.jmp(0x1CAF64_b); + }); + } + + void* get_send_connect_data_stub() + { + return utils::hook::assemble([](utils::hook::assembler& a) + { + const auto false_ = a.newLabel(); + const auto original = a.newLabel(); + + a.mov(ecx, eax); + a.lea(r8, qword_ptr(rbp, 0x4C0)); + a.mov(r9d, ebx); + a.lea(rdx, qword_ptr(rsp, 0x30)); + + a.pushad64(); + a.call_aligned(send_connect_data); + a.test(al, al); + a.popad64(); + + a.mov(rbx, qword_ptr(rsp, 0x9F0)); + a.jmp(0x12D446_b); }); } } @@ -212,25 +234,20 @@ namespace auth // Patch steam id bit check if (game::environment::is_sp()) { - utils::hook::jump(0x140475C17, 0x140475C6A); // H1(1.4) - utils::hook::jump(0x140476AFF, 0x140476B40); // H1(1.4) - utils::hook::jump(0x140476FA4, 0x140476FF2); // H1(1.4) + utils::hook::jump(0x4FA1B3_b, 0x4FA21A_b, true); + utils::hook::jump(0x4FB272_b, 0x4FB2B7_b, true); + utils::hook::jump(0x4FB781_b, 0x4FB7D3_b, true); } else { - utils::hook::jump(0x140571E07, 0x140571E5A); // H1(1.4) - utils::hook::jump(0x14004B223, 0x14004B4F2); // H1(1.4) - utils::hook::jump(0x14004B4AD, 0x14004B4F2); // H1(1.4) - utils::hook::jump(0x140572F6F, 0x140572FB0); // H1(1.4) - utils::hook::jump(0x140573470, 0x1405734B6); // H1(1.4) + // kill "disconnected from steam" error + utils::hook::nop(0x1D61DF_b, 0x11); - utils::hook::jump(0x140488BC1, get_direct_connect_stub(), true); // H1(1.4) - utils::hook::call(0x140250ED2, send_connect_data_stub); // H1(1.4) + utils::hook::jump(0x1CAE70_b, get_direct_connect_stub(), true); + utils::hook::jump(0x12D426_b, get_send_connect_data_stub(), true); - // Skip checks for sending connect packet - utils::hook::jump(0x1402508FC, 0x140250946); // Don't instantly timeout the connecting client ? not sure about this - utils::hook::set(0x14025136B, 0xC3); + utils::hook::set(0x12D93C_b, 0xC3); } command::add("guid", []() diff --git a/src/client/component/binding.cpp b/src/client/component/binding.cpp index e40b7f42..76ba64ee 100644 --- a/src/client/component/binding.cpp +++ b/src/client/component/binding.cpp @@ -15,7 +15,7 @@ namespace binding int get_num_keys() { - return SELECT_VALUE(102, 103); + return 110; } int key_write_bindings_to_buffer_stub(int /*localClientNum*/, char* buffer, const int buffer_size) @@ -83,7 +83,7 @@ namespace binding int key_get_binding_for_cmd_stub(const char* command) { // original binds - for (auto i = 0; i <= get_num_keys(); i++) + for (auto i = 0; i < get_num_keys(); i++) { if (game::command_whitelist[i] && !strcmp(command, game::command_whitelist[i])) { @@ -103,7 +103,7 @@ namespace binding if (static_cast(key) < custom_binds.size() && !custom_binds[key].empty()) { - game::Cbuf_AddText(local_client_num, utils::string::va("%s\n", custom_binds[key].data())); + game::Cbuf_AddText(local_client_num, 0, utils::string::va("%s\n", custom_binds[key].data())); } return; @@ -124,13 +124,13 @@ namespace binding } // write all bindings to config file - utils::hook::call(SELECT_VALUE(0x1401881DB, 0x14025032F), key_write_bindings_to_buffer_stub); // H1(1.4) + utils::hook::jump(SELECT_VALUE(0x1AC980_b, 0x199ED0_b), key_write_bindings_to_buffer_stub); // links a custom command to an index - utils::hook::jump(SELECT_VALUE(0x140343C00, 0x1404041E0), key_get_binding_for_cmd_stub); // H1(1.4) + utils::hook::jump(SELECT_VALUE(0x377280_b, 0x1572B0_b), key_get_binding_for_cmd_stub); // execute custom binds - cl_execute_key_hook.create(SELECT_VALUE(0x140183C70, 0x14024ACF0), &cl_execute_key_stub); // H1(1.4) + cl_execute_key_hook.create(SELECT_VALUE(0x1A8350_b, 0x130610_b), &cl_execute_key_stub); } }; } diff --git a/src/client/component/bots.cpp b/src/client/component/bots.cpp index 5901e5d4..896f00a7 100644 --- a/src/client/component/bots.cpp +++ b/src/client/component/bots.cpp @@ -95,7 +95,7 @@ namespace bots num_bots = atoi(params.get(1)); } - num_bots = std::min(num_bots, *game::mp::svs_numclients);; + num_bots = std::min(num_bots, *game::mp::svs_numclients); for (auto i = 0; i < num_bots; i++) { @@ -106,4 +106,4 @@ namespace bots }; } -REGISTER_COMPONENT(bots::component) \ No newline at end of file +REGISTER_COMPONENT(bots::component) diff --git a/src/client/component/branding.cpp b/src/client/component/branding.cpp index 2f74693b..1474cd5c 100644 --- a/src/client/component/branding.cpp +++ b/src/client/component/branding.cpp @@ -27,14 +27,28 @@ namespace branding const auto* const build_num = ui_get_formatted_build_number_hook.invoke(); return utils::string::va("%s (%s)", VERSION, build_num); } + + void draw_branding() + { + const auto font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20); + if (font) + { + game::R_AddCmdDrawText("H1-Mod: " VERSION, 0x7FFFFFFF, font, 10.f, + 5.f + static_cast(font->pixelHeight), 1.f, 1.f, 0.0f, color, 0); + } + } } class component final : public component_interface { public: + void post_start() override + { + scheduler::loop(draw_branding, scheduler::pipeline::renderer); + } + void post_unpack() override { - if (game::environment::is_dedi()) { return; @@ -46,20 +60,12 @@ namespace branding localized_strings::override("MENU_MULTIPLAYER_CAPS", "H1-MOD: MULTIPLAYER"); } - dvars::override::set_string("version", utils::string::va("H1-Mod %s", VERSION)); + //dvars::override::set_string("version", utils::string::va("H1-Mod %s", VERSION)); ui_get_formatted_build_number_hook.create( - SELECT_VALUE(0x1403B1C40, 0x1404E74C0), ui_get_formatted_build_number_stub); - - scheduler::loop([]() - { - const auto font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20); - - game::R_AddCmdDrawText("H1-Mod: " VERSION, 0x7FFFFFFF, font, 10.f, - 5.f + static_cast(font->pixelHeight), 1.f, 1.f, 0.0f, color, 0); - }, scheduler::pipeline::renderer); + SELECT_VALUE(0x406EC0_b, 0x1DF300_b), ui_get_formatted_build_number_stub); } }; } -REGISTER_COMPONENT(branding::component) +REGISTER_COMPONENT(branding::component) \ No newline at end of file diff --git a/src/client/component/bullet.cpp b/src/client/component/bullet.cpp new file mode 100644 index 00000000..d58e42b1 --- /dev/null +++ b/src/client/component/bullet.cpp @@ -0,0 +1,46 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include + +namespace bullet +{ + namespace + { + game::dvar_t* bg_surface_penetration = nullptr; + utils::hook::detour bg_get_surface_penetration_depth_hook; + + float bg_get_surface_penetration_depth_stub(game::Weapon weapon, bool is_alternate, int surface_type) + { + if (bg_surface_penetration->current.value > 0.0f) + { + return bg_surface_penetration->current.value; + } + + return bg_get_surface_penetration_depth_hook.invoke(weapon, is_alternate, surface_type); + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + if (game::environment::is_sp()) + { + return; + } + + bg_surface_penetration = dvars::register_float("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"); + + bg_get_surface_penetration_depth_hook.create(0x2E1110_b, &bg_get_surface_penetration_depth_stub); + } + }; +} + +REGISTER_COMPONENT(bullet::component) diff --git a/src/client/component/chat.cpp b/src/client/component/chat.cpp index 912624f5..e78550b7 100644 --- a/src/client/component/chat.cpp +++ b/src/client/component/chat.cpp @@ -30,14 +30,14 @@ namespace chat } // use better font - utils::hook::call(0x1400AA831, ui_get_font_handle_stub); - utils::hook::call(0x14024800C, ui_get_font_handle_stub); - utils::hook::call(0x14024F573, ui_get_font_handle_stub); + utils::hook::inject(0x0F6F61_b, reinterpret_cast(0x2E6F588_b)); + utils::hook::inject(0x18A980_b, reinterpret_cast(0x2E6F588_b)); + utils::hook::call(0x33EDEC_b, ui_get_font_handle_stub); // set text style to 0 (non-blurry) - utils::hook::set(0x14024F5DD, 0); - utils::hook::set(0x1400AAA1C, 0); - utils::hook::set(0x14024802E, 0); + utils::hook::set(0x18A9F2_b, 0); + utils::hook::set(0x0F7151_b, 0); + utils::hook::set(0x33EE0E_b, 0); localized_strings::override("EXE_SAY", "^3Match^7"); localized_strings::override("EXE_SAYTEAM", "^5Team^7"); diff --git a/src/client/component/colors.cpp b/src/client/component/colors.cpp index 75eff23a..7016caf5 100644 --- a/src/client/component/colors.cpp +++ b/src/client/component/colors.cpp @@ -96,7 +96,7 @@ namespace colors const size_t unk, const size_t unk2) { // CL_GetClientName (CL_GetClientNameAndClantag?) - const auto result = utils::hook::invoke(0x14025BAA0, local_client_num, index, buf, size, unk, unk2); + const auto result = utils::hook::invoke(0x343BA0_b, local_client_num, index, buf, size, unk, unk2); utils::string::strip(buf, buf, size); @@ -109,11 +109,11 @@ namespace colors if (index == '8') { - *color = *reinterpret_cast(SELECT_VALUE(0x14F142FF8, 0x14FE70634)); // H1(1.4) + *color = *reinterpret_cast(SELECT_VALUE(0x0, 0xEA749B4_b)); // 1.15 } else if (index == '9') { - *color = *reinterpret_cast(SELECT_VALUE(0x14F142FFC, 0x14FE70638)); // H1(1.4) + *color = *reinterpret_cast(SELECT_VALUE(0x0, 0xEA749B8_b)); // 1.15 } else if (index == ':') { @@ -121,7 +121,7 @@ namespace colors } else if (index == ';') { - *color = *reinterpret_cast(SELECT_VALUE(0x14F143004, 0x14FE70640)); // H1(1.4) + *color = *reinterpret_cast(SELECT_VALUE(0x0, 0xEA749C0_b)); // 1.15 } else if (index == '<') { @@ -147,17 +147,17 @@ namespace colors if (!game::environment::is_sp()) { // allows colored name in-game - utils::hook::jump(0x140503810, com_clean_name_stub); // H1(1.4) + utils::hook::jump(0x5AEDF0_b, com_clean_name_stub, true); // don't apply colors to overhead names - utils::hook::call(0x1400AB416, get_client_name_stub); // H1(1.4) + utils::hook::call(0xF7B85_b, get_client_name_stub); // patch I_CleanStr - utils::hook::jump(0x140503D00, i_clean_str_stub); // H1(1.4) + utils::hook::jump(0x5AF2E0_b, i_clean_str_stub, true); } // force new colors - utils::hook::jump(SELECT_VALUE(0x140524BD0, 0x1406206A0), rb_lookup_color_stub); // H1(1.4) + utils::hook::jump(SELECT_VALUE(0x5B17E0_b, 0x6C9460_b), rb_lookup_color_stub, true); // add colors add(0, 0, 0); // 0 - Black diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp index 5f50c25e..458ed522 100644 --- a/src/client/component/command.cpp +++ b/src/client/component/command.cpp @@ -6,6 +6,7 @@ #include "game_console.hpp" #include "fastfiles.hpp" #include "scheduler.hpp" +#include "logfile.hpp" #include "game/game.hpp" #include "game/dvars.hpp" @@ -22,6 +23,7 @@ namespace command namespace { utils::hook::detour client_command_hook; + utils::hook::detour parse_commandline_hook; std::unordered_map> handlers; std::unordered_map> handlers_sv; @@ -39,6 +41,11 @@ namespace command void client_command(const int client_num) { + if (!logfile::client_command_stub(client_num)) + { + return; + } + params_sv params = {}; const auto command = utils::string::to_lower(params[0]); @@ -63,8 +70,8 @@ namespace command static std::string comand_line_buffer = GetCommandLineA(); auto* command_line = comand_line_buffer.data(); - auto& com_num_console_lines = *reinterpret_cast(0x142623FB4); - auto* com_console_lines = reinterpret_cast(0x142623FC0); + auto& com_num_console_lines = *reinterpret_cast(0x35634B8_b); + auto* com_console_lines = reinterpret_cast(0x35634C0_b); auto inq = false; com_console_lines[0] = command_line; @@ -96,7 +103,7 @@ namespace command void parse_commandline_stub() { parse_command_line(); - utils::hook::invoke(0x1400D8210); + parse_commandline_hook.invoke(); } game::dvar_t* dvar_command_stub() @@ -114,11 +121,11 @@ namespace command { if (args.size() == 1) { - const auto current = game::Dvar_ValueToString(dvar, dvar->current); - const auto reset = game::Dvar_ValueToString(dvar, dvar->reset); + const auto current = game::Dvar_ValueToString(dvar, true, dvar->current); + const auto reset = game::Dvar_ValueToString(dvar, true, dvar->reset); - console::info("\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX\n", - args[0], current, reset, dvar->hash); + console::info("\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX type: %i\n", + args[0], current, reset, dvar->hash, dvar->type); const auto dvar_info = dvars::dvar_get_description(args[0]); @@ -127,7 +134,7 @@ namespace command } else { - char command[0x1000] = { 0 }; + char command[0x1000] = {0}; game::Dvar_GetCombinedString(command, 1); game::Dvar_SetCommand(dvar->hash, "", command); } @@ -340,8 +347,8 @@ namespace command // parse the commandline if it's not parsed parse_command_line(); - auto& com_num_console_lines = *reinterpret_cast(0x142623FB4); - auto* com_console_lines = reinterpret_cast(0x142623FC0); + auto& com_num_console_lines = *reinterpret_cast(0x35634B8_b); + auto* com_console_lines = reinterpret_cast(0x35634C0_b); for (int i = 0; i < com_num_console_lines; i++) { @@ -454,8 +461,6 @@ namespace command void add(const char* name, const std::function& callback) { - static game::cmd_function_s cmd_test; - const auto command = utils::string::to_lower(name); if (handlers.find(command) == handlers.end()) @@ -493,7 +498,7 @@ namespace command } else { - game::Cbuf_AddText(0, command.data()); + game::Cbuf_AddText(0, 0, command.data()); } } @@ -508,12 +513,12 @@ namespace command } else { - utils::hook::call(0x1400D728F, parse_commandline_stub); - utils::hook::jump(0x14041D750, dvar_command_stub); - + parse_commandline_hook.create(0x157D50_b, parse_commandline_stub); add_commands_mp(); } + utils::hook::jump(SELECT_VALUE(0x3A7C80_b, 0x4E9F40_b), dvar_command_stub, true); + add_commands_generic(); } @@ -620,7 +625,7 @@ namespace command if (dvar->type != game::dvar_type::string && dvar->type != game::dvar_type::enumeration) { - console::info("%s is not a string-based dvar\n", dvar->hash); + console::info("%s is not a string-based dvar\n", name); return; } @@ -680,16 +685,6 @@ namespace command toggle_client_flag(2, "ufo"); }); - add("give", [](const params& params) - { - if (!game::SV_Loaded()) - { - return; - } - - cmd_give_weapon(0, params.get_all()); - }); - add("dropweapon", [](const params& params) { if (!game::SV_Loaded()) @@ -719,11 +714,21 @@ namespace command cmd_kill(0); }); + + add("give", [](const params& params) + { + if (!game::SV_Loaded()) + { + return; + } + + cmd_give_weapon(0, params.get_all()); + }); } static void add_commands_mp() { - client_command_hook.create(0x140336000, &client_command); + client_command_hook.create(0x4132E0_b, &client_command); add_sv("god", [](const int client_num, const params_sv&) { @@ -772,7 +777,7 @@ namespace command return; } - toggle_client_flag(client_num, 2, "noclip"); + toggle_client_flag(client_num, 2, "ufo"); }); add_sv("give", [](const int client_num, const params_sv& params) diff --git a/src/client/component/console.cpp b/src/client/component/console.cpp index 174beaf8..62b9d16e 100644 --- a/src/client/component/console.cpp +++ b/src/client/component/console.cpp @@ -1,14 +1,17 @@ #include #include "console.hpp" #include "loader/component_loader.hpp" + #include "game/game.hpp" + #include "command.hpp" +#include "rcon.hpp" #include -#include -#include #include +#define OUTPUT_HANDLE GetStdHandle(STD_OUTPUT_HANDLE) + namespace game_console { void print(int type, const std::string& data); @@ -18,26 +21,40 @@ namespace console { namespace { - using message_queue = std::queue; - utils::concurrency::container messages; + utils::hook::detour printf_hook; + std::recursive_mutex print_mutex; - bool native_console() + struct { - static const auto flag = utils::flags::has_flag("nativeconsole"); - return flag; + bool kill; + std::thread thread; + HANDLE kill_event; + char buffer[512]{}; + int cursor; + std::int32_t history_index = -1; + std::deque history{}; + } con{}; + + void set_cursor_pos(int x) + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + info.dwCursorPosition.X = static_cast(x); + SetConsoleCursorPosition(OUTPUT_HANDLE, info.dwCursorPosition); } - void hide_console() + void show_cursor(const bool show) { - auto* const con_window = GetConsoleWindow(); + CONSOLE_CURSOR_INFO info{}; + GetConsoleCursorInfo(OUTPUT_HANDLE, &info); + info.bVisible = show; + SetConsoleCursorInfo(OUTPUT_HANDLE, &info); + } - DWORD process; - GetWindowThreadProcessId(con_window, &process); - - if (!native_console() && (process == GetCurrentProcessId() || IsDebuggerPresent())) - { - ShowWindow(con_window, SW_HIDE); - } + template + int invoke_printf(const char* fmt, Args&&... args) + { + return printf_hook.invoke(fmt, std::forward(args)...); } std::string format(va_list* ap, const char* message) @@ -45,244 +62,266 @@ namespace console static thread_local char buffer[0x1000]; const auto count = _vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer), message, *ap); + if (count < 0) + { + return {}; + } - if (count < 0) return {}; return {buffer, static_cast(count)}; } - void dispatch_message(const int type, const std::string& message) + uint8_t get_attribute(const int type) { - if (native_console()) + switch (type) { - printf("%s\n", message.data()); + case con_type_info: + return 7; // white + case con_type_warning: + return 6; // yellow + case con_type_error: + return 4; // red + case con_type_debug: + return 3; // cyan + } + + return 7; + } + + void update() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + invoke_printf("%s", con.buffer); + set_cursor_pos(con.cursor); + show_cursor(true); + } + + void clear_output() + { + std::lock_guard _0(print_mutex); + + show_cursor(false); + set_cursor_pos(0); + + for (auto i = 0; i < std::strlen(con.buffer); i++) + { + invoke_printf(" "); + } + + set_cursor_pos(con.cursor); + show_cursor(true); + } + + int dispatch_message(const int type, const std::string& message) + { + if (rcon::message_redirect(message)) + { + return 0; + } + + std::lock_guard _0(print_mutex); + + clear_output(); + set_cursor_pos(0); + + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(type)); + const auto res = invoke_printf("%s", message.data()); + SetConsoleTextAttribute(OUTPUT_HANDLE, get_attribute(con_type_info)); + + game_console::print(type, message); + + if (message.size() <= 0 || message[message.size() - 1] != '\n') + { + invoke_printf("\n"); + } + + update(); + return res; + } + + void clear() + { + std::lock_guard _0(print_mutex); + + clear_output(); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + + con.cursor = 0; + set_cursor_pos(0); + } + + size_t get_max_input_length() + { + CONSOLE_SCREEN_BUFFER_INFO info{}; + GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info); + const auto columns = static_cast(info.srWindow.Right - info.srWindow.Left - 1); + return std::max(size_t(0), std::min(columns, sizeof(con.buffer))); + } + + void handle_resize() + { + clear(); + update(); + } + + void handle_input(const INPUT_RECORD record) + { + if (record.EventType == WINDOW_BUFFER_SIZE_EVENT) + { + handle_resize(); return; } - game_console::print(type, message); - messages.access([&message](message_queue& msgs) + if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown) { - msgs.emplace(message); - }); - } - - void append_text(const char* text) - { - dispatch_message(con_type_info, text); - } - } - - class component final : public component_interface - { - public: - component() - { - hide_console(); - - if (native_console()) - { - setvbuf(stdout, nullptr, _IONBF, 0); - setvbuf(stderr, nullptr, _IONBF, 0); - } - else - { - (void)_pipe(this->handles_, 1024, _O_TEXT); - (void)_dup2(this->handles_[1], 1); - (void)_dup2(this->handles_[1], 2); - } - } - - void post_start() override - { - this->terminate_runner_ = false; - - this->console_runner_ = utils::thread::create_named_thread("Console IO", [this] - { - if (native_console()) - { - this->native_input(); - } - else - { - this->runner(); - } - }); - } - - void pre_destroy() override - { - this->terminate_runner_ = true; - - printf("\r\n"); - _flushall(); - - if (this->console_runner_.joinable()) - { - this->console_runner_.join(); + return; } - if (this->console_thread_.joinable()) + std::lock_guard _0(print_mutex); + + const auto key = record.Event.KeyEvent.wVirtualKeyCode; + switch (key) { - this->console_thread_.join(); + case VK_UP: + { + if (++con.history_index >= con.history.size()) + { + con.history_index = static_cast(con.history.size()) - 1; + } + + clear(); + + if (con.history_index != -1) + { + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); + } + + update(); + break; } - -#ifndef NATIVE_CONSOLE - _close(this->handles_[0]); - _close(this->handles_[1]); -#endif - - messages.access([&](message_queue& msgs) + case VK_DOWN: { - msgs = {}; - }); - } - - void post_unpack() override - { - // Redirect input (]command) - utils::hook::jump(SELECT_VALUE(0x1403E34C0, 0x1405141E0), append_text); // H1(1.4) - - this->initialize(); - } - - private: - volatile bool console_initialized_ = false; - volatile bool terminate_runner_ = false; - - std::thread console_runner_; - std::thread console_thread_; - - int handles_[2]{}; - - void initialize() - { - this->console_thread_ = utils::thread::create_named_thread("Console", [this]() - { - if (!native_console() && (game::environment::is_dedi() || !utils::flags::has_flag("noconsole"))) + if (--con.history_index < -1) { - game::Sys_ShowConsole(); + con.history_index = -1; } - if (!game::environment::is_dedi()) + clear(); + + if (con.history_index != -1) { - // Hide that shit - ShowWindow(console::get_window(), SW_MINIMIZE); + strncpy_s(con.buffer, con.history.at(con.history_index).data(), sizeof(con.buffer)); + con.cursor = static_cast(strlen(con.buffer)); } + update(); + break; + } + case VK_LEFT: + { + if (con.cursor > 0) { - messages.access([&](message_queue&) + con.cursor--; + set_cursor_pos(con.cursor); + } + + break; + } + case VK_RIGHT: + { + if (con.cursor < std::strlen(con.buffer)) + { + con.cursor++; + set_cursor_pos(con.cursor); + } + + break; + } + case VK_RETURN: + { + if (con.history_index != -1) + { + const auto itr = con.history.begin() + con.history_index; + + if (*itr == con.buffer) { - this->console_initialized_ = true; - }); - } - - MSG msg; - while (!this->terminate_runner_) - { - if (PeekMessageA(&msg, nullptr, NULL, NULL, PM_REMOVE)) - { - if (msg.message == WM_QUIT) - { - command::execute("quit", false); - break; - } - - TranslateMessage(&msg); - DispatchMessage(&msg); - } - else - { - this->log_messages(); - std::this_thread::sleep_for(1ms); + con.history.erase(con.history.begin() + con.history_index); } } - }); - } - - void log_messages() - { - /*while*/ - if (this->console_initialized_ && !messages.get_raw().empty()) - { - std::queue message_queue_copy; + if (con.buffer[0]) { - messages.access([&](message_queue& msgs) - { - message_queue_copy = std::move(msgs); - msgs = {}; - }); + con.history.push_front(con.buffer); } - while (!message_queue_copy.empty()) + if (con.history.size() > 10) { - log_message(message_queue_copy.front()); - message_queue_copy.pop(); + con.history.erase(con.history.begin() + 10); } + + con.history_index = -1; + + game::Cbuf_AddText(0, 0, con.buffer); + + con.cursor = 0; + + clear_output(); + invoke_printf("]%s\r\n", con.buffer); + strncpy_s(con.buffer, "", sizeof(con.buffer)); + break; } - - fflush(stdout); - fflush(stderr); - } - - static void log_message(const std::string& message) - { - OutputDebugStringA(message.data()); - game::Conbuf_AppendText(message.data()); - } - - void runner() - { - char buffer[1024]; - - while (!this->terminate_runner_ && this->handles_[0]) + case VK_BACK: { - const auto len = _read(this->handles_[0], buffer, sizeof(buffer)); - if (len > 0) + if (con.cursor <= 0) { - dispatch_message(con_type_info, std::string(buffer, len)); - } - else - { - std::this_thread::sleep_for(1ms); + break; } + + clear_output(); + + std::memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor, + strlen(con.buffer) + 1 - con.cursor); + con.cursor--; + + update(); + break; } - - std::this_thread::yield(); - } - - void native_input() - { - std::string cmd; - - while (!this->terminate_runner_) + default: { - std::getline(std::cin, cmd); - command::execute(cmd); + const auto c = record.Event.KeyEvent.uChar.AsciiChar; + if (!c) + { + break; + } + + if (std::strlen(con.buffer) + 1 >= get_max_input_length()) + { + break; + } + + std::memmove(con.buffer + con.cursor + 1, + con.buffer + con.cursor, std::strlen(con.buffer) + 1 - con.cursor); + con.buffer[con.cursor] = c; + con.cursor++; + + update(); + break; + } } - - std::this_thread::yield(); } - }; - HWND get_window() - { - return *reinterpret_cast((SELECT_VALUE(0x14CF56C00, 0x14DDFC2D0))); // H1(1.4) - } + int __cdecl printf_stub(const char* fmt, ...) + { + va_list ap; + va_start(ap, fmt); + const auto result = format(&ap, fmt); + va_end(ap); - void set_title(std::string title) - { - SetWindowText(get_window(), title.data()); - } - - void set_size(const int width, const int height) - { - RECT rect; - GetWindowRect(get_window(), &rect); - - SetWindowPos(get_window(), nullptr, rect.left, rect.top, width, height, 0); - - auto* const logo_window = *reinterpret_cast(SELECT_VALUE(0x14CF56C10, 0x14DDFC2E0)); // H1(1.4) - SetWindowPos(logo_window, nullptr, 5, 5, width - 25, 60, 0); + return dispatch_message(con_type_info, result); + } } void print(const int type, const char* fmt, ...) @@ -294,6 +333,86 @@ namespace console dispatch_message(type, result); } + + class component final : public component_interface + { + public: + component() + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + } + + void post_unpack() override + { + printf_hook.create(printf, printf_stub); + + ShowWindow(GetConsoleWindow(), SW_SHOW); + SetConsoleTitle("H1-Mod"); + + con.kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + + con.thread = utils::thread::create_named_thread("Console", []() + { + const auto handle = GetStdHandle(STD_INPUT_HANDLE); + HANDLE handles[2] = {handle, con.kill_event}; + MSG msg{}; + + INPUT_RECORD record{}; + DWORD num_events{}; + + while (!con.kill) + { + const auto result = MsgWaitForMultipleObjects(2, handles, FALSE, INFINITE, QS_ALLINPUT); + if (con.kill) + { + return; + } + + switch (result) + { + case WAIT_OBJECT_0: + { + if (!ReadConsoleInput(handle, &record, 1, &num_events) || num_events == 0) + { + break; + } + + handle_input(record); + break; + } + case WAIT_OBJECT_0 + 1: + { + if (!PeekMessageA(&msg, GetConsoleWindow(), NULL, NULL, PM_REMOVE)) + { + break; + } + + if (msg.message == WM_QUIT) + { + command::execute("quit", false); + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + break; + } + } + } + }); + } + + void pre_destroy() override + { + con.kill = true; + SetEvent(con.kill_event); + + if (con.thread.joinable()) + { + con.thread.join(); + } + } + }; } REGISTER_COMPONENT(console::component) diff --git a/src/client/component/console.hpp b/src/client/component/console.hpp index 302951a8..4101a3a6 100644 --- a/src/client/component/console.hpp +++ b/src/client/component/console.hpp @@ -9,6 +9,7 @@ namespace console enum console_type { con_type_error = 1, + con_type_debug = 2, con_type_warning = 3, con_type_info = 7 }; @@ -21,6 +22,14 @@ namespace console print(con_type_error, fmt, std::forward(args)...); } + template + void debug(const char* fmt, Args&&... args) + { +#ifdef DEBUG + print(con_type_debug, fmt, std::forward(args)...); +#endif + } + template void warn(const char* fmt, Args&&... args) { diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index d012c16c..bf0e361e 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -26,7 +26,7 @@ namespace dedicated initialized = true; // R_LoadGraphicsAssets - utils::hook::invoke(0x1405DF4B0); + utils::hook::invoke(0x686310_b); } void send_heartbeat() @@ -83,8 +83,8 @@ namespace dedicated { if (game::Live_SyncOnlineDataFlags(0) == 0) { - game::Cbuf_AddText(client, command); - game::Cbuf_AddText(client, "\n"); + game::Cbuf_AddText(client, 0, command); + game::Cbuf_AddText(client, 0, "\n"); } else { @@ -99,8 +99,8 @@ namespace dedicated for (const auto& command : queue) { - game::Cbuf_AddText(0, command.data()); - game::Cbuf_AddText(0, "\n"); + game::Cbuf_AddText(0, 0, command.data()); + game::Cbuf_AddText(0, 0, "\n"); } } @@ -126,12 +126,16 @@ namespace dedicated void kill_server() { - for (auto i = 0; i < *game::mp::svs_numclients; ++i) + const auto* svs_clients = *game::mp::svs_clients; + if (svs_clients != nullptr) { - if (game::mp::svs_clients[i].header.state >= 3) + for (auto i = 0; i < *game::mp::svs_numclients; ++i) { - game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE, - utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); + if (svs_clients[i].header.state >= 3) + { + game::SV_GameSendServerCommand(i, game::SV_CMD_CAN_IGNORE, + utils::string::va("r \"%s\"", "EXE_ENDOFGAME")); + } } } @@ -156,6 +160,23 @@ namespace dedicated game::Com_Error(game::ERR_DROP, "%s", buffer); } + + utils::hook::detour ui_set_active_menu_hook; + void ui_set_active_menu_stub(void* localClientNum, int menu) + { + static auto done = false; + if (done && (menu == 6 || menu == 7)) + { + return; + } + + if (menu == 6 || menu == 7) + { + done = true; + } + + ui_set_active_menu_hook.invoke(localClientNum, menu); + } } void initialize() @@ -197,96 +218,113 @@ namespace dedicated dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_READ); // Stop crashing from sys_errors - utils::hook::jump(0x140511520, sys_error_stub); + utils::hook::jump(0x1D8710_b, sys_error_stub, true); // Hook R_SyncGpu - utils::hook::jump(0x1405E12F0, sync_gpu_stub); + utils::hook::jump(0x688620_b, sync_gpu_stub, true); - utils::hook::jump(0x140254800, init_dedicated_server); + utils::hook::jump(0x135600_b, init_dedicated_server, true); // delay startup commands until the initialization is done - utils::hook::call(0x1400D72D6, execute_startup_command); + utils::hook::jump(0x157DD3_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.lea(r8, qword_ptr(rsp, 0x20)); + a.xor_(ecx, ecx); - // delay console commands until the initialization is done - utils::hook::call(0x1400D808C, execute_console_command); - utils::hook::nop(0x1400D80A4, 5); + a.pushad64(); + a.call_aligned(execute_startup_command); + a.popad64(); + + a.jmp(0x157DDF_b); + }), true);// + + // delay console commands until the initialization is done // COULDN'T FOUND + // utils::hook::call(0x1400D808C, execute_console_command); + // utils::hook::nop(0x1400D80A4, 5); // patch GScr_SetDynamicDvar to behave better - gscr_set_dynamic_dvar_hook.create(0x14036B600, &gscr_set_dynamic_dvar); + gscr_set_dynamic_dvar_hook.create(0x43CF60_b, &gscr_set_dynamic_dvar); - utils::hook::nop(0x1404ED90E, 5); // don't load config file - utils::hook::nop(0x140403D92, 5); // ^ - utils::hook::set(0x1400DC1D0, 0xC3); // don't save config file - utils::hook::set(0x140274710, 0xC3); // disable self-registration - utils::hook::set(0x140515890, 0xC3); // init sound system (1) - utils::hook::set(0x1406574F0, 0xC3); // init sound system (2) - utils::hook::set(0x140620D10, 0xC3); // render thread - utils::hook::set(0x14025B850, 0xC3); // called from Com_Frame, seems to do renderer stuff - utils::hook::set(0x1402507B0, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly - utils::hook::set(0x1405D5178, 0x00); // r_loadForRenderer default to 0 - utils::hook::set(0x14050C2D0, 0xC3); // recommended settings check - TODO: Check hook - utils::hook::set(0x140514C00, 0xC3); // some mixer-related function called on shutdown - utils::hook::set(0x140409830, 0xC3); // dont load ui gametype stuff + utils::hook::nop(0x189514_b, 248); // don't load config file + utils::hook::nop(0x156C46_b, 5); // ^ + utils::hook::set(0x17F470_b, 0xC3); // don't save config file + utils::hook::set(0x351AA0_b, 0xC3); // disable self-registration + utils::hook::set(0x5BF4E0_b, 0xC3); // init sound system (1) + utils::hook::set(0x701820_b, 0xC3); // init sound system (2) + utils::hook::set(0x701850_b, 0xC3); // init sound system (3) + utils::hook::set(0x6C9B10_b, 0xC3); // render thread + utils::hook::set(0x343950_b, 0xC3); // called from Com_Frame, seems to do renderer stuff + utils::hook::set(0x12CCA0_b, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly + utils::hook::set(0x67ADCE_b, 0x00); // r_loadForRenderer default to 0 + utils::hook::set(0x5B7AF0_b, 0xC3); // recommended settings check + utils::hook::set(0x5BE850_b, 0xC3); // some mixer-related function called on shutdown + utils::hook::set(0x4DEA50_b, 0xC3); // dont load ui gametype stuff - utils::hook::nop(0x140481B06, 6); // unknown check in SV_ExecuteClientMessage - utils::hook::nop(0x140480FAC, 4); // allow first slot to be occupied - utils::hook::nop(0x14025619B, 2); // properly shut down dedicated servers - utils::hook::nop(0x14025615E, 2); // ^ - utils::hook::nop(0x1402561C0, 5); // don't shutdown renderer + utils::hook::nop(0x54ED81_b, 6); // unknown check in SV_ExecuteClientMessage + utils::hook::nop(0x54E337_b, 4); // allow first slot to be occupied + utils::hook::nop(0x13ABCB_b, 2); // properly shut down dedicated servers + utils::hook::nop(0x13AB8E_b, 2); // ^ + utils::hook::nop(0x13ABF0_b, 5); // don't shutdown renderer - utils::hook::set(0x140091840, 0xC3); // something to do with blendShapeVertsView - utils::hook::nop(0x140659A0D, 8); // sound thing + utils::hook::set(0xAA290_b, 0xC3); // something to do with blendShapeVertsView + utils::hook::nop(0x70465D_b, 8); // sound thing // (COULD NOT FIND IN H1) // utils::hook::set(0x1404D6960, 0xC3); // cpu detection stuff? - utils::hook::set(0x1405E97F0, 0xC3); // gfx stuff during fastfile loading - utils::hook::set(0x1405E9700, 0xC3); // ^ - utils::hook::set(0x1405E9790, 0xC3); // ^ - utils::hook::set(0x1402C1180, 0xC3); // ^ - utils::hook::set(0x1405E9750, 0xC3); // ^ - utils::hook::set(0x1405AD5B0, 0xC3); // directx stuff - utils::hook::set(0x1405DB150, 0xC3); // ^ - utils::hook::set(0x140625220, 0xC3); // ^ - mutex - utils::hook::set(0x1405DB650, 0xC3); // ^ + utils::hook::set(0x690F30_b, 0xC3); // gfx stuff during fastfile loading + utils::hook::set(0x690E00_b, 0xC3); // ^ + utils::hook::set(0x690ED0_b, 0xC3); // ^ + utils::hook::set(0x39B980_b, 0xC3); // ^ + utils::hook::set(0x690E50_b, 0xC3); // ^ + utils::hook::set(0x651BA0_b, 0xC3); // directx stuff + utils::hook::set(0x681950_b, 0xC3); // ^ + utils::hook::set(0x6CE390_b, 0xC3); // ^ - mutex + utils::hook::set(0x681ED0_b, 0xC3); // ^ - utils::hook::set(0x14008B5F0, 0xC3); // rendering stuff - utils::hook::set(0x1405DB8B0, 0xC3); // ^ - utils::hook::set(0x1405DB9C0, 0xC3); // ^ - utils::hook::set(0x1405DC050, 0xC3); // ^ - utils::hook::set(0x1405DCBA0, 0xC3); // ^ - utils::hook::set(0x1405DD240, 0xC3); // ^ + utils::hook::set(0x0A3CD0_b, 0xC3); // rendering stuff + utils::hook::set(0x682150_b, 0xC3); // ^ + utils::hook::set(0x682260_b, 0xC3); // ^ + utils::hook::set(0x6829C0_b, 0xC3); // ^ + utils::hook::set(0x6834A0_b, 0xC3); // ^ + utils::hook::set(0x683B40_b, 0xC3); // ^ // shaders - utils::hook::set(0x1400916A0, 0xC3); // ^ - utils::hook::set(0x140091610, 0xC3); // ^ - utils::hook::set(0x14061ACC0, 0xC3); // ^ - mutex + utils::hook::set(0x0AA090_b, 0xC3); // ^ + utils::hook::set(0x0A9FE0_b, 0xC3); // ^ + utils::hook::set(0x6C38D0_b, 0xC3); // ^ - mutex - utils::hook::set(0x140516080, 0xC3); // idk - utils::hook::set(0x1405AE5F0, 0xC3); // ^ + utils::hook::set(0x5BFD10_b, 0xC3); // idk + utils::hook::set(0x652E10_b, 0xC3); // ^ - utils::hook::set(0x1405E0B30, 0xC3); // R_Shutdown - utils::hook::set(0x1405AE400, 0xC3); // shutdown stuff - utils::hook::set(0x1405E0C00, 0xC3); // ^ - utils::hook::set(0x1405DFE50, 0xC3); // ^ + utils::hook::set(0x687D20_b, 0xC3); // R_Shutdown + utils::hook::set(0x652BA0_b, 0xC3); // shutdown stuff + utils::hook::set(0x687DF0_b, 0xC3); // ^ + utils::hook::set(0x686DE0_b, 0xC3); // ^ // utils::hook::set(0x1404B67E0, 0xC3); // sound crashes (H1 - questionable, function looks way different) - utils::hook::set(0x14048B660, 0xC3); // disable host migration + utils::hook::set(0x556250_b, 0xC3); // disable host migration - utils::hook::set(0x14042B2E0, 0xC3); // render synchronization lock - utils::hook::set(0x14042B210, 0xC3); // render synchronization unlock + utils::hook::set(0x4F7C10_b, 0xC3); // render synchronization lock + utils::hook::set(0x4F7B40_b, 0xC3); // render synchronization unlock - utils::hook::set(0x140176D2D, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua + utils::hook::set(0x27AA9D_b, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua + utils::hook::set(0x27AAC5_b, 0xEB); // LUI: Unable to start the LUI system due to errors in depot.lua + utils::hook::set(0x27AADC_b, 0xEB); // ^ - utils::hook::nop(0x140506ECE, 5); // Disable sound pak file loading - utils::hook::nop(0x140506ED6, 2); // ^ - utils::hook::set(0x1402C5910, 0xC3); // Disable image pak file loading + utils::hook::nop(0x5B25BE_b, 5); // Disable sound pak file loading + utils::hook::nop(0x5B25C6_b, 2); // ^ + utils::hook::set(0x3A0BA0_b, 0xC3); // Disable image pak file loading // Reduce min required memory - utils::hook::set(0x14050C717, 0x80000000); + utils::hook::set(0x5B7F37_b, 0x80000000); - utils::hook::set(0x1402BF7F0, 0xC3); // some loop - utils::hook::set(0x14007E150, 0xC3); // related to shader caching / techsets / fastfiles + utils::hook::set(0x399E10_b, 0xC3); // some loop + utils::hook::set(0x1D48B0_b, 0xC3); // related to shader caching / techsets / fastfilesc + utils::hook::set(0x3A1940_b, 0xC3); // DB_ReadPackedLoadedSounds + + // Workaround for server spamming 'exec default_xboxlive.cfg' when not running + ui_set_active_menu_hook.create(0x1E4D80_b, ui_set_active_menu_stub); // initialize the game after onlinedataflags is 32 (workaround) scheduler::schedule([=]() @@ -318,14 +356,14 @@ namespace dedicated execute_startup_command_queue(); execute_console_command_queue(); - // Send heartbeat to dpmaster + // Send heartbeat to master scheduler::once(send_heartbeat, scheduler::pipeline::server); scheduler::loop(send_heartbeat, scheduler::pipeline::server, 10min); command::add("heartbeat", send_heartbeat); }, scheduler::pipeline::main, 1s); command::add("killserver", kill_server); - com_quit_f_hook.create(0x1400DA640, &kill_server); + com_quit_f_hook.create(0x17CD00_b, &kill_server); } }; } diff --git a/src/client/component/dedicated_info.cpp b/src/client/component/dedicated_info.cpp index 55973193..fa5c9a34 100644 --- a/src/client/component/dedicated_info.cpp +++ b/src/client/component/dedicated_info.cpp @@ -1,5 +1,4 @@ #include -#include "console.hpp" #include "loader/component_loader.hpp" #include "game/game.hpp" #include "scheduler.hpp" @@ -20,9 +19,9 @@ namespace dedicated_info scheduler::loop([]() { auto* sv_running = game::Dvar_FindVar("sv_running"); - if (!sv_running || !sv_running->current.enabled) + if (!sv_running || !sv_running->current.enabled || (*game::mp::svs_clients) == nullptr) { - console::set_title("H1-Mod Dedicated Server"); + SetConsoleTitle("H1-Mod Dedicated Server"); return; } @@ -33,12 +32,14 @@ namespace dedicated_info auto bot_count = 0; auto client_count = 0; - for (auto i = 0; i < sv_maxclients->current.integer; i++) + const auto svs_clients = *game::mp::svs_clients; + + for (auto i = 0; i < *game::mp::svs_numclients; i++) { - auto* client = &game::mp::svs_clients[i]; + const auto client = svs_clients[i]; auto* self = &game::mp::g_entities[i]; - if (client->header.state >= 1 && self && self->client) + if (client.header.state >= 1 && self && self->client) { client_count++; if (game::SV_BotIsBot(i)) @@ -54,9 +55,10 @@ namespace dedicated_info utils::string::strip(sv_hostname->current.string, cleaned_hostname.data(), static_cast(strlen(sv_hostname->current.string)) + 1); - console::set_title(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(), - mapname->current.string, client_count, - sv_maxclients->current.integer, bot_count)); + SetConsoleTitle(utils::string::va("%s on %s [%d/%d] (%d)", cleaned_hostname.data(), + mapname->current.string, client_count, + sv_maxclients->current.integer, bot_count) + ); }, scheduler::pipeline::main, 1s); } }; diff --git a/src/client/component/demonware.cpp b/src/client/component/demonware.cpp index d74889a5..5b42983c 100644 --- a/src/client/component/demonware.cpp +++ b/src/client/component/demonware.cpp @@ -297,7 +297,7 @@ namespace demonware if (server) { - server->handle_input(buf, len, {s, to, tolen}); + server->handle_input(buf, len, { s, to, tolen }); return len; } @@ -425,9 +425,9 @@ namespace demonware } } - void bd_logger_stub(char* a1, void* a2, void* a3, void* a4, const char* function, ...) + void bd_logger_stub() { - + //printf("logged\n"); } #ifdef DEBUG @@ -481,6 +481,39 @@ namespace demonware printf("bdAuth: Unknown error\n"); } #endif + + utils::hook::detour handle_auth_reply_hook; + bool handle_auth_reply_stub(void* a1, void* a2, void* a3) + { + // Skip bdAuth::validateResponseSignature + utils::hook::set(0x7D4AB0_b, 0xC301B0); + // Skip bdAuth::processPlatformData + utils::hook::set(0x7D55C0_b, 0xC301B0); + + return handle_auth_reply_hook.invoke(a1, a2, a3); + } + + void* allocate_somewhere_near(uint8_t* base_address) + { + const size_t PAGE_SIZE = 0x1000; + size_t offset = 0; + while (true) + { + offset += PAGE_SIZE; + auto res = VirtualAlloc(base_address - offset, PAGE_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); + if (res) + { + std::memset(res, 0, PAGE_SIZE); + return res; + } + } + } + + void request_start_match_stub() + { + const auto* args = "StartServer"; + game::UI_RunMenuScript(0, &args); + } } class component final : public component_interface @@ -505,6 +538,11 @@ namespace demonware void post_load() override { + if (game::environment::is_sp()) + { + return; + } + server_thread = utils::thread::create_named_thread("Demonware", server_main); } @@ -537,52 +575,44 @@ namespace demonware void post_unpack() override { - utils::hook::jump(SELECT_VALUE(0x140610320, 0x1407400B0), bd_logger_stub); - if (game::environment::is_sp()) { - utils::hook::set(0x1405FCA00, 0xC3); // bdAuthSteam - utils::hook::set(0x140333A00, 0xC3); // dwNet + utils::hook::set(0x68DDA0_b, 0xC3); // bdAuthSteam + utils::hook::set(0x366600_b, 0xC3); // dwNet return; } - utils::hook::set(0x140715039, 0x0); // CURLOPT_SSL_VERIFYPEER - utils::hook::set(0x140715025, 0xAF); // CURLOPT_SSL_VERIFYHOST - utils::hook::set(0x14095433C, 0x0); // HTTPS -> HTTP + utils::hook::set(0x7C0AD9_b, 0x0); // CURLOPT_SSL_VERIFYPEER + utils::hook::set(0x7C0AC5_b, 0xAF); // CURLOPT_SSL_VERIFYHOST + utils::hook::set(0xA1327C_b, 0x0); // HTTPS -> HTTP - //HTTPS -> HTTP - utils::hook::inject(0x14006DDA9, "http://prod.umbrella.demonware.net/v1.0/"); - utils::hook::inject(0x14006E11C, "http://prod.umbrella.demonware.net/v1.0/"); - utils::hook::inject(0x14006E2FB, "http://prod.umbrella.demonware.net/v1.0/"); - utils::hook::inject(0x14006E9A9, "http://prod.uno.demonware.net/v1.0/"); - utils::hook::inject(0x14006ED49, "http://prod.uno.demonware.net/v1.0/"); - utils::hook::inject(0x140728170, "http://%s:%d/auth/"); + std::memcpy(reinterpret_cast(0x8D0298_b), + "http://prod.umbrella.demonware.net/v1.0/", sizeof("http://prod.umbrella.demonware.net/v1.0/")); + std::memcpy(reinterpret_cast(0x8D05A8_b), + "http://prod.uno.demonware.net/v1.0/", sizeof("http://prod.uno.demonware.net/v1.0/")); + std::memcpy(reinterpret_cast(0x9EDB08_b), "http://%s:%d/auth/", sizeof("http://%s:%d/auth/")); - utils::hook::set(0x14047F290, 0xC3); // SV_SendMatchData - utils::hook::set(0x140598990, 0xC3); // Live_CheckForFullDisconnect + // utils::hook::set(0x19F8C0_b, 0xC3); // SV_SendMatchData, not sure + utils::hook::nop(0x19BB67_b, 5); // LiveStorage_SendMatchDataComplete + utils::hook::nop(0x19BC3F_b, 5); // LiveStorage_GettingStoreConfigComplete probably + utils::hook::set(0x1A3340_b, 0xC3); // Live_CheckForFullDisconnect -#ifdef DEBUG - // yes - utils::hook::call(0x140727BEB, l); - utils::hook::call(0x140727AFC, i); - utils::hook::call(0x140727E49, h); - utils::hook::call(0x140727E30, g); - utils::hook::call(0x140727E37, f); - utils::hook::call(0x140727DF2, e); - utils::hook::call(0x140727DF9, d); - utils::hook::call(0x140727CFC, c); - utils::hook::call(0x140727C82, b); - utils::hook::call(0x140727E6A, a); -#endif - // Checks X-Signature header or something - utils::hook::set(0x140728380, 0xC301B0); - // Checks extended_data and extra_data in json object - utils::hook::set(0x140728E90, 0xC301B0); - // Update check - utils::hook::set(0x1403A5390, 0xC301B0); - - // Remove some while loop in demonware that freezes the rendering for a few secs at launch - utils::hook::nop(0x14057DBC5, 5); + // Remove some while loop that freezes the rendering for a few secs while connecting + utils::hook::nop(0x625555_b, 5); + + handle_auth_reply_hook.create(0x7AC600_b, handle_auth_reply_stub); + + // Skip update check in Live_SyncOnlineDataFlags + utils::hook::set(0x47A6D0_b, 0xC301B0); + // Remove update failed popup + utils::hook::set(0x47B2B0_b, 0xC301B0); + + // xpartygo -> just start the match + utils::hook::jump(0x355B80_b, request_start_match_stub, true); + + utils::hook::set(0x396AD0_b, 0xC301B0); // DB_IsZoneLoaded("ffotd") + utils::hook::set(0x4DD600_b, 0xC300B0); // dont use ffotd + utils::hook::set(0x4DD5B0_b, 0xC300B0); // dont dl ffotd } void pre_destroy() override diff --git a/src/client/component/discord.cpp b/src/client/component/discord.cpp index 3698e30f..e0693fa5 100644 --- a/src/client/component/discord.cpp +++ b/src/client/component/discord.cpp @@ -10,7 +10,6 @@ #include "materials.hpp" #include "discord.hpp" -#include "ui_scripting.hpp" #include "game/ui_scripting/execution.hpp" #include @@ -53,7 +52,6 @@ namespace discord discord_presence.matchSecret = ""; discord_presence.joinSecret = ""; discord_presence.partyId = ""; - discord_presence.state = ""; } else { @@ -103,7 +101,16 @@ namespace discord discord_presence.partyPrivacy = DISCORD_PARTY_PUBLIC; } - discord_presence.partySize = *reinterpret_cast(0x1429864C4); + const auto client_state = *game::mp::client_state; + if (client_state != nullptr) + { + discord_presence.partySize = client_state->num_players; + } + else + { + discord_presence.partySize = 0; + } + discord_presence.partyMax = max_clients; discord_presence.state = clean_hostname; discord_presence.largeImageKey = map; @@ -188,9 +195,13 @@ namespace discord handlers.ready = ready; handlers.errored = errored; handlers.disconnected = errored; - handlers.joinGame = join_game; handlers.spectateGame = nullptr; - handlers.joinRequest = join_request; + + if (game::environment::is_mp()) + { + handlers.joinGame = join_game; + handlers.joinRequest = join_request; + } Discord_Initialize("947125042930667530", &handlers, 1, nullptr); @@ -285,4 +296,4 @@ namespace discord }; } -REGISTER_COMPONENT(discord::component) \ No newline at end of file +REGISTER_COMPONENT(discord::component) diff --git a/src/client/component/dvar_cheats.cpp b/src/client/component/dvar_cheats.cpp index be9232b3..66c49e6c 100644 --- a/src/client/component/dvar_cheats.cpp +++ b/src/client/component/dvar_cheats.cpp @@ -83,92 +83,45 @@ namespace dvar_cheats return true; } - const auto dvar_flag_checks_stub = utils::hook::assemble([](utils::hook::assembler& a) + void* get_dvar_flag_checks_stub() { - const auto can_set_value = a.newLabel(); - const auto zero_source = a.newLabel(); - - a.pushad64(); - a.mov(r8, rdi); - a.mov(edx, esi); - a.mov(rcx, rbx); - a.call_aligned(apply_sv_cheats); //check if we are setting sv_cheats - a.popad64(); - a.cmp(esi, 0); - a.jz(zero_source); //if the SetSource is 0 (INTERNAL) ignore flag checks - - a.pushad64(); - a.mov(edx, esi); //source - a.mov(rcx, rbx); //dvar - a.call_aligned(dvar_flag_checks); //protect read/write/cheat/replicated dvars - a.cmp(al, 1); - a.jz(can_set_value); - - // if we get here, we are non-zero source and CANNOT set values - a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag - a.jmp(0x1404FDCAB); - - // if we get here, we are non-zero source and CAN set values - a.bind(can_set_value); - a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag - a.cmp(esi, 1); - a.jmp(0x1404FDA22); - - // if we get here, we are zero source and ignore flags - a.bind(zero_source); - a.jmp(0x1404FDA62); - }); - - void cg_set_client_dvar_from_server(const int local_client_num, void* cg, const char* dvar_id, const char* value) - { - const auto* dvar = game::Dvar_FindVar(dvar_id); - if (dvar) + return utils::hook::assemble([](utils::hook::assembler& a) { - // If we send as string, it can't be set with source SERVERCMD because the game only allows that source on real server cmd dvars. - // Just use external instead as if it was being set by the console - game::Dvar_SetFromStringByNameFromSource(dvar_id, value, game::DvarSetSource::DVAR_SOURCE_EXTERNAL); - } - else - { - // Not a dvar name, assume it is an id and the game will handle normally - game::CG_SetClientDvarFromServer(local_client_num, cg, dvar_id, value); - } + const auto can_set_value = a.newLabel(); + const auto zero_source = a.newLabel(); + + a.pushad64(); + a.mov(r8, rdi); + a.mov(edx, esi); + a.mov(rcx, rbx); + a.call_aligned(apply_sv_cheats); // check if we are setting sv_cheats + a.popad64(); + a.cmp(esi, 0); + a.jz(zero_source); // if the SetSource is 0 (INTERNAL) ignore flag checks + + a.pushad64(); + a.mov(edx, esi); // source + a.mov(rcx, rbx); // dvar + a.call_aligned(dvar_flag_checks); // protect read/write/cheat/replicated dvars + a.cmp(al, 1); + a.jz(can_set_value); + + // if we get here, we are non-zero source and CANNOT set values + a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag + a.jmp(0x18655C_b); + + // if we get here, we are non-zero source and CAN set values + a.bind(can_set_value); + a.popad64(); // if I do this before the jz it won't work. for some reason the popad64 is affecting the ZR flag + a.cmp(esi, 1); + a.jmp(0x1861EE_b); + + // if we get here, we are zero source and ignore flags + a.bind(zero_source); + a.jmp(0x18628F_b); + }); } - void set_client_dvar_by_string(const int entity_num, const char* value) - { - 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); - } - - const auto player_cmd_set_client_dvar = utils::hook::assemble([](utils::hook::assembler& a) - { - const auto set_by_string = a.newLabel(); - - a.pushad64(); - - // check if we didn't find a network dvar index - a.mov(ecx, dword_ptr(rsp, 0x8C8)); - a.cmp(ecx, 0); - a.je(set_by_string); - - // we found an index, handle normally - a.popad64(); - a.mov(r8d, ptr(rsp, 0x848)); - a.lea(r9, ptr(rsp, 0x30)); - a.jmp(0x1402E2E57); - - // no index, let's send the dvar as a string - a.bind(set_by_string); - a.movzx(ecx, word_ptr(rsp, 0x8C0)); //entity_num - a.lea(rdx, ptr(rsp, 0xB0)); //value - a.call_aligned(set_client_dvar_by_string); - a.popad64(); - a.jmp(0x1402E2E7D); - }); - class component final : public component_interface { public: @@ -179,20 +132,13 @@ namespace dvar_cheats return; } - utils::hook::nop(0x1404FDA0D, 4); // let our stub handle zero-source sets - utils::hook::jump(0x1404FDA14, dvar_flag_checks_stub, true); // check extra dvar flags when setting values - - // utils::hook::nop(0x14032AACC, 5); // remove error in PlayerCmd_SetClientDvar if setting a non-network dvar - // utils::hook::set(0x14032AA9B, 0xEB); - // don't check flags on the dvars, send any existing dvar instead - // utils::hook::jump(0x14032AB14, player_cmd_set_client_dvar, true); // send non-network dvars as string - // utils::hook::call(0x1401BB782, cg_set_client_dvar_from_server); - // check for dvars being sent as string before parsing ids + utils::hook::nop(0x1861D4_b, 8); // let our stub handle zero-source sets + utils::hook::jump(0x1861DF_b, get_dvar_flag_checks_stub(), true); // check extra dvar flags when setting values scheduler::once([]() { dvars::register_bool("sv_cheats", false, game::DvarFlags::DVAR_FLAG_REPLICATED, - "Allow cheat commands and dvars on this server"); + "Allow cheat commands and dvars on this server"); }, scheduler::pipeline::main); } }; diff --git a/src/client/component/dvars.cpp b/src/client/component/dvars.cpp index d9c77fdf..3b285f20 100644 --- a/src/client/component/dvars.cpp +++ b/src/client/component/dvars.cpp @@ -35,7 +35,6 @@ namespace dvars struct dvar_vector3 : dvar_base { - float x{}; float y{}; float z{}; @@ -43,6 +42,12 @@ namespace dvars float max{}; }; + struct dvar_enum : dvar_base + { + const char* const* value_list{}; + int default_index{}; + }; + struct dvar_int : dvar_base { int value{}; @@ -139,6 +144,7 @@ namespace dvars static std::unordered_map register_string_overrides; static std::unordered_map register_vector2_overrides; static std::unordered_map register_vector3_overrides; + static std::unordered_map register_enum_overrides; static std::unordered_map set_bool_overrides; static std::unordered_map set_float_overrides; @@ -210,6 +216,16 @@ namespace dvars register_vector3_overrides[name] = std::move(values); } + void register_enum(const std::string& name, /*const char* const* value_list, int default_index,*/ + const unsigned int flags) + { + dvar_enum values; + //values.value_list = value_list; + //values.default_index = default_index; + values.flags = flags; + register_enum_overrides[name] = std::move(values); + } + void set_bool(const std::string& name, const bool value) { set_bool_overrides[name] = value; @@ -237,11 +253,15 @@ namespace dvars } utils::hook::detour dvar_register_bool_hook; + utils::hook::detour dvar_register_bool_hashed_hook; utils::hook::detour dvar_register_float_hook; + utils::hook::detour dvar_register_float_hashed_hook; utils::hook::detour dvar_register_int_hook; + utils::hook::detour dvar_register_int_hashed_hook; utils::hook::detour dvar_register_string_hook; utils::hook::detour dvar_register_vector2_hook; utils::hook::detour dvar_register_vector3_hook; + utils::hook::detour dvar_register_enum_hook; utils::hook::detour dvar_set_bool_hook; utils::hook::detour dvar_set_float_hook; @@ -261,6 +281,18 @@ namespace dvars return dvar_register_bool_hook.invoke(hash, name, value, flags); } + game::dvar_t* dvar_register_bool_hashed(const int hash, const char* name, bool value, unsigned int flags) + { + auto* var = find_dvar(override::register_bool_overrides, hash); + if (var) + { + value = var->value; + flags = var->flags; + } + + return dvar_register_bool_hashed_hook.invoke(hash, name, value, flags); + } + game::dvar_t* dvar_register_float(const int hash, const char* name, float value, float min, float max, unsigned int flags) { auto* var = find_dvar(override::register_float_overrides, hash); @@ -275,6 +307,20 @@ namespace dvars return dvar_register_float_hook.invoke(hash, name, value, min, max, flags); } + game::dvar_t* dvar_register_float_hashed(const int hash, const char* name, float value, float min, float max, unsigned int flags) + { + auto* var = find_dvar(override::register_float_overrides, hash); + if (var) + { + value = var->value; + min = var->min; + max = var->max; + flags = var->flags; + } + + return dvar_register_float_hashed_hook.invoke(hash, name, value, min, max, flags); + } + game::dvar_t* dvar_register_int(const int hash, const char* name, int value, int min, int max, unsigned int flags) { auto* var = find_dvar(override::register_int_overrides, hash); @@ -289,6 +335,20 @@ namespace dvars return dvar_register_int_hook.invoke(hash, name, value, min, max, flags); } + game::dvar_t* dvar_register_int_hashed(const int hash, const char* name, int value, int min, int max, unsigned int flags) + { + auto* var = find_dvar(override::register_int_overrides, hash); + if (var) + { + value = var->value; + min = var->min; + max = var->max; + flags = var->flags; + } + + return dvar_register_int_hashed_hook.invoke(hash, name, value, min, max, flags); + } + game::dvar_t* dvar_register_string(const int hash, const char* name, const char* value, unsigned int flags) { auto* var = find_dvar(override::register_string_overrides, hash); @@ -334,6 +394,19 @@ namespace dvars return dvar_register_vector3_hook.invoke(hash, name, x, y, z, min, max, flags); } + game::dvar_t* dvar_register_enum(const int hash, const char* name, const char* const value_list, int default_index, unsigned int flags) + { + auto* var = find_dvar(override::register_enum_overrides, hash); + if (var) + { + //value_list = var->value_list; + //default_index = var->default_index; + flags = var->flags; + } + + return dvar_register_enum_hook.invoke(hash, name, value_list, default_index, flags); + } + void dvar_set_bool(game::dvar_t* dvar, bool boolean) { const auto disabled = find_dvar(disable::set_bool_disables, dvar->hash); @@ -424,18 +497,26 @@ namespace dvars public: void post_unpack() override { - dvar_register_bool_hook.create(SELECT_VALUE(0x1403C47E0, 0x1404FA540), &dvar_register_bool); - dvar_register_float_hook.create(SELECT_VALUE(0x1403C4BB0, 0x1404FA910), &dvar_register_float); - dvar_register_int_hook.create(SELECT_VALUE(0x1403C4CC0, 0x1404FAA20), &dvar_register_int); - dvar_register_string_hook.create(SELECT_VALUE(0x1403C4DA0, 0x1404FAB00), &dvar_register_string); - dvar_register_vector2_hook.create(SELECT_VALUE(0x1403C4E80, 0x1404FABE0), &dvar_register_vector2); - dvar_register_vector3_hook.create(SELECT_VALUE(0x1403C4FC0, 0x1404FACE0), &dvar_register_vector3); + dvar_register_bool_hook.create(SELECT_VALUE(0x419220_b, 0x182340_b), &dvar_register_bool); + dvar_register_float_hook.create(SELECT_VALUE(0x4195F0_b, 0x1827F0_b), &dvar_register_float); + dvar_register_int_hook.create(SELECT_VALUE(0x419700_b, 0x182A10_b), &dvar_register_int); + dvar_register_string_hook.create(SELECT_VALUE(0x4197E0_b, 0x182BD0_b), &dvar_register_string); + dvar_register_vector2_hook.create(SELECT_VALUE(0x4198C0_b, 0x182CB0_b), &dvar_register_vector2); + dvar_register_vector3_hook.create(SELECT_VALUE(0x419A00_b, 0x182DB0_b), &dvar_register_vector3); + dvar_register_enum_hook.create(SELECT_VALUE(0x419500_b, 0x182700_b), &dvar_register_enum); - dvar_set_bool_hook.create(SELECT_VALUE(0x1403C7020, 0x1404FCDF0), &dvar_set_bool); - dvar_set_float_hook.create(SELECT_VALUE(0x1403C7420, 0x1404FD360), &dvar_set_float); - dvar_set_int_hook.create(SELECT_VALUE(0x1403C76C0, 0x1404FD5E0), &dvar_set_int); - dvar_set_string_hook.create(SELECT_VALUE(0x1403C7900, 0x1404FD8D0), &dvar_set_string); - dvar_set_from_string_hook.create(SELECT_VALUE(0x1403C7620, 0x1404FD520), &dvar_set_from_string); + if (!game::environment::is_sp()) + { + dvar_register_bool_hashed_hook.create(SELECT_VALUE(0x0, 0x182420_b), &dvar_register_bool_hashed); + dvar_register_float_hashed_hook.create(SELECT_VALUE(0x0, 0x182900_b), &dvar_register_float_hashed); + dvar_register_int_hashed_hook.create(SELECT_VALUE(0x0, 0x182AF0_b), &dvar_register_int_hashed); + } + + dvar_set_bool_hook.create(SELECT_VALUE(0x41B820_b, 0x185520_b), &dvar_set_bool); + dvar_set_float_hook.create(SELECT_VALUE(0x41BC60_b, 0x185AA0_b), &dvar_set_float); + dvar_set_int_hook.create(SELECT_VALUE(0x41BEE0_b, 0x185D10_b), &dvar_set_int); + dvar_set_string_hook.create(SELECT_VALUE(0x41C0F0_b, 0x186080_b), &dvar_set_string); + dvar_set_from_string_hook.create(SELECT_VALUE(0x41BE20_b, 0x185C60_b), &dvar_set_from_string); } }; } diff --git a/src/client/component/dvars.hpp b/src/client/component/dvars.hpp index 66d6912d..419530e2 100644 --- a/src/client/component/dvars.hpp +++ b/src/client/component/dvars.hpp @@ -18,6 +18,7 @@ namespace dvars void register_string(const std::string& name, const std::string& value, const unsigned int flags); void register_vec2(const std::string& name, float x, float y, float min, float max, const unsigned int flags); void register_vec3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags); + void register_enum(const std::string& name, /*const char* const* value_list, int default_index,*/ const unsigned int flags); void set_bool(const std::string& name, bool boolean); void set_float(const std::string& name, float fl); diff --git a/src/client/component/exception.cpp b/src/client/component/exception.cpp index ae8f77db..598473ad 100644 --- a/src/client/component/exception.cpp +++ b/src/client/component/exception.cpp @@ -84,9 +84,10 @@ namespace exception void display_error_dialog() { - std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p.\n" + std::string error_str = utils::string::va("Fatal error (0x%08X) at 0x%p (0x%p).\n" "A minidump has been written.\n\n", - exception_data.code, exception_data.address); + exception_data.code, exception_data.address, + reinterpret_cast(exception_data.address) - game::base_address); if (!system_check::is_valid()) { @@ -176,6 +177,7 @@ namespace exception line("Clean game: "s + (system_check::is_valid() ? "Yes" : "No")); line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode)); line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress)); + line(utils::string::va("Base: 0x%llX", game::base_address)); #pragma warning(push) #pragma warning(disable: 4996) diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp index 22ec023c..1ff9a8ae 100644 --- a/src/client/component/fastfiles.cpp +++ b/src/client/component/fastfiles.cpp @@ -1,12 +1,16 @@ #include #include "loader/component_loader.hpp" -#include "fastfiles.hpp" +#include "game/dvars.hpp" + +#include "fastfiles.hpp" #include "command.hpp" #include "console.hpp" #include #include +#include +#include namespace fastfiles { @@ -15,16 +19,69 @@ namespace fastfiles namespace { utils::hook::detour db_try_load_x_file_internal_hook; + utils::hook::detour db_find_xasset_header_hook; + game::dvar_t* g_dump_scripts; void db_try_load_x_file_internal(const char* zone_name, const int flags) { - printf("Loading fastfile %s\n", zone_name); + console::info("Loading fastfile %s\n", zone_name); current_fastfile.access([&](std::string& fastfile) { fastfile = zone_name; }); db_try_load_x_file_internal_hook.invoke(zone_name, flags); } + + void dump_gsc_script(const std::string& name, game::XAssetHeader header) + { + if (!g_dump_scripts->current.enabled) + { + return; + } + + std::string buffer; + buffer.append(header.scriptfile->name, strlen(header.scriptfile->name) + 1); + buffer.append(reinterpret_cast(&header.scriptfile->compressedLen), 4); + buffer.append(reinterpret_cast(&header.scriptfile->len), 4); + buffer.append(reinterpret_cast(&header.scriptfile->bytecodeLen), 4); + buffer.append(header.scriptfile->buffer, header.scriptfile->compressedLen); + buffer.append(header.scriptfile->bytecode, header.scriptfile->bytecodeLen); + + const auto out_name = utils::string::va("gsc_dump/%s.gscbin", name.data()); + utils::io::write_file(out_name, buffer); + + console::info("Dumped %s\n", out_name); + } + + game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default) + { + const auto start = game::Sys_Milliseconds(); + const auto result = db_find_xasset_header_hook.invoke(type, name, allow_create_default); + const auto diff = game::Sys_Milliseconds() - start; + + if (type == game::XAssetType::ASSET_TYPE_SCRIPTFILE) + { + dump_gsc_script(name, result); + } + + if (diff > 100) + { + console::print( + result.data == nullptr + ? console::con_type_error + : console::con_type_warning, + "Waited %i msec for %sasset \"%s\", of type \"%s\"\n", + diff, + result.data == nullptr + ? "missing " + : "", + name, + game::g_assetNames[type] + ); + } + + return result; + } } std::string get_current_fastfile() @@ -51,7 +108,11 @@ namespace fastfiles void post_unpack() override { db_try_load_x_file_internal_hook.create( - SELECT_VALUE(0x1401CDDD0, 0x1402BFFE0), &db_try_load_x_file_internal); + SELECT_VALUE(0x1F5700_b, 0x39A620_b), &db_try_load_x_file_internal); + + db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); + + g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts"); } }; } diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp index d1827936..15d11826 100644 --- a/src/client/component/filesystem.cpp +++ b/src/client/component/filesystem.cpp @@ -12,36 +12,6 @@ namespace filesystem { - namespace - { - bool custom_path_registered = false; - - std::string get_binary_directory() - { - const auto dir = game_module::get_host_module().get_folder(); - return utils::string::replace(dir, "/", "\\"); - } - - void register_custom_path_stub(const char* path, const char* dir) - { - if (!custom_path_registered) - { - custom_path_registered = true; - - const auto launcher_dir = get_binary_directory(); - game::FS_AddLocalizedGameDirectory(launcher_dir.data(), "data"); - } - - game::FS_AddLocalizedGameDirectory(path, dir); - } - - void fs_startup_stub(const char* gamename) - { - custom_path_registered = false; - game::FS_Startup(gamename); - } - } - file::file(std::string name) : name_(std::move(name)) { @@ -110,19 +80,6 @@ namespace filesystem public: void post_unpack() override { - // Set fs_basegame - dvars::override::register_string("fs_basegame", "h1-mod", game::DVAR_FLAG_WRITE); - - utils::hook::call(SELECT_VALUE(0x1403B76E2, 0x1404ED3E2), fs_startup_stub); - if (game::environment::is_mp()) - { - utils::hook::call(0x1404ED823, fs_startup_stub); - } - - utils::hook::call(SELECT_VALUE(0x1403B8D31, 0x1404EE3D0), register_custom_path_stub); - utils::hook::call(SELECT_VALUE(0x1403B8D51, 0x1404EE3F0), register_custom_path_stub); - utils::hook::call(SELECT_VALUE(0x1403B8D90, 0x1404EE42F), register_custom_path_stub); - get_search_paths().insert("."); get_search_paths().insert("h1-mod"); get_search_paths().insert("data"); diff --git a/src/client/component/fonts.cpp b/src/client/component/fonts.cpp index 32f55399..07f447ca 100644 --- a/src/client/component/fonts.cpp +++ b/src/client/component/fonts.cpp @@ -114,7 +114,7 @@ namespace fonts } data_.fonts.clear(); - utils::hook::set(SELECT_VALUE(0x14F09DBB8, 0x14FD61EE8), 0); // reset registered font count + utils::hook::set(SELECT_VALUE(0xF793E38_b, 0xE962188_b), 0); // reset registered font count }); } @@ -128,7 +128,7 @@ namespace fonts return; } - utils::hook::call(SELECT_VALUE(0x1404D41B6, 0x1405D9296), db_find_xasset_header_stub); + utils::hook::call(SELECT_VALUE(0x55C596_b, 0x67F6E6_b), db_find_xasset_header_stub); } }; } diff --git a/src/client/component/fps.cpp b/src/client/component/fps.cpp index 49caf5bf..dc721c3b 100644 --- a/src/client/component/fps.cpp +++ b/src/client/component/fps.cpp @@ -5,6 +5,7 @@ #include "game/game.hpp" #include "game/dvars.hpp" +#include "dvars.hpp" #include #include @@ -14,6 +15,8 @@ namespace fps { namespace { + utils::hook::detour sub_5D6810_hook; + game::dvar_t* cg_drawfps; game::dvar_t* cg_drawping; @@ -87,8 +90,6 @@ namespace fps cg_perf.previous_ms = cg_perf.current_ms; perf_calc_fps(&cg_perf, cg_perf.frame_ms); - - utils::hook::invoke(SELECT_VALUE(0x1405487A0, 0x1406575A0)); } void cg_draw_fps() @@ -98,23 +99,26 @@ namespace fps const auto fps = fps::get_fps(); const auto font = game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25); - const auto fps_string = utils::string::va("%i", fps); + if (font) + { + const auto fps_string = utils::string::va("%i", fps); - const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 15.0f) - game::R_TextWidth( - fps_string, 0x7FFFFFFF, font); - const auto y = font->pixelHeight + 10.f; + const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 15.0f) - + game::R_TextWidth(fps_string, 0x7FFFFFFF, font); + const auto y = font->pixelHeight + 10.f; - const auto fps_color = fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad); - game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, font, x, y, 1.f, 1.f, 0.0f, fps_color, 6); + const auto fps_color = fps >= 60 ? fps_color_good : (fps >= 30 ? fps_color_ok : fps_color_bad); + game::R_AddCmdDrawText(fps_string, 0x7FFFFFFF, font, x, y, 1.f, 1.f, 0.0f, fps_color, 6); + } } } void cg_draw_ping() { - if (cg_drawping->current.integer > 0 && game::CL_IsCgameInitialized() && !game::VirtualLobby_Loaded()) + if (cg_drawping->current.integer > 0 && game::CL_IsCgameInitialized() && !game::VirtualLobby_Loaded() && *game::mp::client_state) { const auto font = game::R_RegisterFont("fonts/consolefont", 20); - const auto ping_string = utils::string::va("Ping: %i", *game::mp::ping); + const auto ping_string = utils::string::va("Ping: %i", (*game::mp::client_state)->ping); const auto x = (game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 375.0f) - game::R_TextWidth( ping_string, 0x7FFFFFFF, font); @@ -124,12 +128,17 @@ namespace fps } } - game::dvar_t* cg_draw_fps_register_stub(const char* name, const char** _enum, const int value, unsigned int /*flags*/, - const char* desc) + game::dvar_t* cg_draw_fps_register_stub() { cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second"); return cg_drawfps; } + + void sub_5D6810_stub() + { + perf_update(); + sub_5D6810_hook.invoke(); + } } int get_fps() @@ -151,22 +160,42 @@ namespace fps // fps setup cg_perf.perf_start = std::chrono::high_resolution_clock::now(); - utils::hook::call(SELECT_VALUE(0x14018D261, 0x14025B747), &perf_update); - // change cg_drawfps flags to saved - utils::hook::call(SELECT_VALUE(0x140139F48, 0x140222A46), &cg_draw_fps_register_stub); + if (game::environment::is_mp()) + { + utils::hook::jump(SELECT_VALUE(0, 0x343847_b), utils::hook::assemble([](utils::hook::assembler& a) + { + a.pushad64(); + a.call_aligned(perf_update); + a.popad64(); + + a.call(0x702250_b); + a.mov(edx, 3); + a.xor_(ecx, ecx); + a.jmp(0x343853_b); + }), true); + + // Don't register cg_drawfps + utils::hook::nop(0x31D74F_b, 0x1C); + utils::hook::nop(0x31D76F_b, 0x7); + } + else + { + sub_5D6810_hook.create(0x5D6810_b, sub_5D6810_stub); + + // Don't register cg_drawfps + utils::hook::nop(0x15C97D_b, 0x20); + utils::hook::nop(0x15C9A1_b, 0x7); + } scheduler::loop(cg_draw_fps, scheduler::pipeline::renderer); - if (game::environment::is_sp()) - { - cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second"); - } + cg_drawfps = dvars::register_int("cg_drawFps", 0, 0, 2, game::DVAR_FLAG_SAVED, "Draw frames per second"); if (game::environment::is_mp()) { // fix ping value - utils::hook::nop(0x14025AC41, 2); + utils::hook::nop(0x342C6C_b, 2); cg_drawping = dvars::register_int("cg_drawPing", 0, 0, 1, game::DVAR_FLAG_SAVED, "Choose to draw ping"); diff --git a/src/client/component/game_console.cpp b/src/client/component/game_console.cpp index 26d492ce..ef8b5fda 100644 --- a/src/client/component/game_console.cpp +++ b/src/client/component/game_console.cpp @@ -286,10 +286,10 @@ namespace game_console { const auto offset = (con.screen_max[0] - con.globals.x) / 4.f; - draw_hint_text(0, game::Dvar_ValueToString(dvar, dvar->current), + draw_hint_text(0, game::Dvar_ValueToString(dvar, true, dvar->current), dvars::con_inputDvarValueColor->current.vector, offset); draw_hint_text(1, " default", dvars::con_inputDvarInactiveValueColor->current.vector); - draw_hint_text(1, game::Dvar_ValueToString(dvar, dvar->reset), + draw_hint_text(1, game::Dvar_ValueToString(dvar, true, dvar->reset), dvars::con_inputDvarInactiveValueColor->current.vector, offset); draw_hint_text(2, matches[0].description.data(), color_white, 0); @@ -324,7 +324,7 @@ namespace game_console if (dvar) { - draw_hint_text(static_cast(i), game::Dvar_ValueToString(dvar, dvar->current), + draw_hint_text(static_cast(i), game::Dvar_ValueToString(dvar, true, dvar->current), dvars::con_inputDvarValueColor->current.vector, offset); draw_hint_text(static_cast(i), matches[i].description.data(), @@ -391,7 +391,7 @@ namespace game_console const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f; const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f; - game::R_AddCmdDrawText("H1-Mod 1.4", 0x7FFFFFFF, console_font, x, + game::R_AddCmdDrawText("H1-Mod 1.15", 0x7FFFFFFF, console_font, x, ((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_title, 0); draw_output_scrollbar(x, y, width, height, output); @@ -586,10 +586,13 @@ namespace game_console return false; } - if (game::playerKeys[local_client_num].keys[game::keyNum_t::K_SHIFT].down) + const auto shift_down = game::playerKeys[local_client_num].keys[game::keyNum_t::K_SHIFT].down; + if (shift_down) { if (!(*game::keyCatchers & 1)) + { toggle_console(); + } toggle_console_output(); return false; @@ -680,7 +683,7 @@ namespace game_console if (key == game::keyNum_t::K_ENTER) { - game::Cbuf_AddText(0, utils::string::va("%s \n", fixed_input.data())); + game::Cbuf_AddText(0, 0, utils::string::va("%s \n", fixed_input.data())); if (history_index != -1) { @@ -720,20 +723,17 @@ namespace game_console { return; } - - //scheduler::loop(draw_console, scheduler::pipeline::renderer); } void post_unpack() override { - scheduler::loop(draw_console, scheduler::pipeline::renderer); - - if (game::environment::is_dedi()) { return; } + scheduler::loop(draw_console, scheduler::pipeline::renderer); + // initialize our structs con.cursor = 0; con.visible_line_count = 0; diff --git a/src/client/component/game_module.cpp b/src/client/component/game_module.cpp index bd984624..ad4eb9ce 100644 --- a/src/client/component/game_module.cpp +++ b/src/client/component/game_module.cpp @@ -3,6 +3,7 @@ #include "game_module.hpp" #include +#include namespace game_module { @@ -59,9 +60,9 @@ namespace game_module DWORD __stdcall get_module_file_name_a(HMODULE hmodule, const LPSTR filename, const DWORD size) { - if (!hmodule) + if (!hmodule || utils::nt::library(hmodule) == get_game_module()) { - hmodule = get_game_module(); + hmodule = get_host_module(); } return file_name_a_hook.invoke(hmodule, filename, size); @@ -69,9 +70,9 @@ namespace game_module DWORD __stdcall get_module_file_name_w(HMODULE hmodule, const LPWSTR filename, const DWORD size) { - if (!hmodule) + if (!hmodule || utils::nt::library(hmodule) == get_game_module()) { - hmodule = get_game_module(); + hmodule = get_host_module(); } return file_name_w_hook.invoke(hmodule, filename, size); @@ -90,7 +91,7 @@ namespace game_module utils::nt::library get_game_module() { - static utils::nt::library game{HMODULE(0x140000000)}; + static utils::nt::library game{HMODULE(game::base_address)}; return game; } @@ -110,7 +111,11 @@ namespace game_module void post_load() override { +#ifdef INJECT_HOST_AS_LIB hook_module_resolving(); +#else + assert(get_host_module() == get_game_module()); +#endif } }; } diff --git a/src/client/component/gameplay.cpp b/src/client/component/gameplay.cpp index fbee0b3c..724a5adb 100644 --- a/src/client/component/gameplay.cpp +++ b/src/client/component/gameplay.cpp @@ -16,6 +16,14 @@ namespace gameplay { utils::hook::detour pm_weapon_use_ammo_hook; utils::hook::detour pm_player_trace_hook; + utils::hook::detour pm_crashland_hook; + utils::hook::detour jump_apply_slowdown_hook; + utils::hook::detour stuck_in_client_hook; + utils::hook::detour cm_transformed_capsule_trace_hook; + + utils::hook::detour client_end_frame_hook; + utils::hook::detour g_damage_client_hook; + utils::hook::detour g_damage_hook; game::dvar_t* jump_slowDownEnable; game::dvar_t* jump_enableFallDamage; @@ -24,18 +32,16 @@ namespace gameplay { if (jump_slowDownEnable->current.enabled) { - utils::hook::invoke(0x1401D5360, ps); + jump_apply_slowdown_hook.invoke(ps); } } - int stuck_in_client_stub(void* entity) + void stuck_in_client_stub(void* entity) { if (dvars::g_playerEjection->current.enabled) { - return utils::hook::invoke(0x140326CE0, entity); // StuckInClient + stuck_in_client_hook.invoke(entity); } - - return 0; } void cm_transformed_capsule_trace_stub(game::trace_t* results, const float* start, const float* end, @@ -43,8 +49,8 @@ namespace gameplay { if (dvars::g_playerCollision->current.enabled) { - utils::hook::invoke(0x1403FF860, - results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace + cm_transformed_capsule_trace_hook.invoke(results, start, end, + bounds, capsule, contents, origin, angles); } } @@ -52,7 +58,7 @@ namespace gameplay { if (jump_enableFallDamage->current.enabled) { - utils::hook::invoke(0x1401E2D00, ps, pml); + pm_crashland_hook.invoke(ps, pml); } } @@ -65,58 +71,67 @@ namespace gameplay } } - const auto pm_bouncing_stub_mp = utils::hook::assemble([](utils::hook::assembler& a) + void* pm_bouncing_stub_mp() { - const auto no_bounce = a.newLabel(); - const auto loc_1401EAF9D = a.newLabel(); + return utils::hook::assemble([](utils::hook::assembler& a) + { + const auto no_bounce = a.newLabel(); + const auto loc_2D395D = a.newLabel(); - a.push(rax); + a.push(rax); - a.mov(rax, qword_ptr(reinterpret_cast(&dvars::pm_bouncing))); - a.mov(al, byte_ptr(rax, 0x10)); - a.cmp(byte_ptr(rbp, -0x2D), al); + a.mov(rax, qword_ptr(reinterpret_cast(&dvars::pm_bouncing))); + a.mov(al, byte_ptr(rax, 0x10)); + a.cmp(byte_ptr(rbp, -0x7D), al); - a.pop(rax); - a.jz(no_bounce); - a.jmp(0x1401EB000); + a.pop(rax); + a.jz(no_bounce); + a.jmp(0x2D39C0_b); - a.bind(no_bounce); - a.cmp(dword_ptr(rsp, 0x70), 0); - a.jnz(loc_1401EAF9D); - a.jmp(0x1401EAFF1); + a.bind(no_bounce); + a.cmp(dword_ptr(rsp, 0x44), 0); + a.jnz(loc_2D395D); + a.jmp(0x2D39B1_b); - a.bind(loc_1401EAF9D); - a.jmp(0x1401EAF9D); - }); + a.bind(loc_2D395D); + a.jmp(0x2D395D_b); + }); + } - const auto g_speed_stub = utils::hook::assemble([](utils::hook::assembler& a) + void* g_speed_stub() { - a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_speed))); - a.mov(eax, dword_ptr(rax, 0x10)); + return utils::hook::assemble([](utils::hook::assembler& a) + { + a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_speed))); + a.mov(eax, dword_ptr(rax, 0x10)); - // original code - a.mov(dword_ptr(r14, 0x36), ax); - a.movzx(eax, word_ptr(r14, 0x3A)); + // original code + a.mov(dword_ptr(r14, 0x36), ax); + a.movzx(eax, word_ptr(r14, 0x3A)); - a.jmp(0x140323DBC); - }); + a.jmp(0x4006BC_b); + }); + } - const auto client_end_frame_stub = utils::hook::assemble([](utils::hook::assembler& a) + void* client_end_frame_stub() { - a.push(rax); + return utils::hook::assemble([](utils::hook::assembler& a) + { + a.push(rax); - a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_gravity))); - a.mov(eax, dword_ptr(rax, 0x10)); - a.mov(word_ptr(rbx, 0x34), ax); + a.mov(rax, qword_ptr(reinterpret_cast(&dvars::g_gravity))); + a.mov(eax, dword_ptr(rax, 0x10)); + a.mov(word_ptr(rbx, 0x34), ax); - a.pop(rax); + a.pop(rax); - // Game code hook skipped - a.mov(eax, dword_ptr(rbx, 0x494C)); - a.mov(rdi, rcx); + // Game code hook skipped + a.mov(eax, dword_ptr(rbx, 0x495C)); + a.mov(rdi, rcx); - a.jmp(0x140322F82); - }); + a.jmp(0x3FF822_b); + }); + } void pm_player_trace_stub(game::pmove_t* pm, game::trace_t* trace, const float* f3, const float* f4, const game::Bounds* bounds, int a6, int a7) @@ -135,7 +150,7 @@ namespace gameplay const auto stand = a.newLabel(); const auto allsolid = a.newLabel(); - a.call(qword_ptr(r10, r15)); // Game code + a.call(rsi); // Game code a.push(rax); @@ -151,11 +166,57 @@ namespace gameplay a.jnz(allsolid); a.bind(stand); - a.and_(dword_ptr(rbx, 0x54), 0xFFFFFFFD); - a.jmp(0x1401E1CDF); + a.and_(dword_ptr(r15, 0x54), 0xFFFFFFFD); + a.jmp(0x2C9F9D_b); a.bind(allsolid); - a.jmp(0x1401E1CE1); + a.jmp(0x2C9F9F_b); + }; + + void client_end_frame_stub2(game::mp::gentity_s* entity) + { + client_end_frame_hook.invoke(entity); + + if ((entity->client->flags & 1)) // noclip + { + entity->client->pm_type = 2; + } + else if ((entity->client->flags & 2)) // ufo + { + entity->client->pm_type = 3; + } + } + + void g_damage_client_stub(game::mp::gentity_s* targ, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, + const float* dir, const float* point, int damage, int dflags, int mod, + const unsigned int weapon, bool is_alternate, unsigned int hit_loc, int time_offset) + { + if ((targ->client->flags & 1) || (targ->client->flags & 2)) // noclip, ufo + { + return; + } + + g_damage_client_hook.invoke(targ, inflictor, attacker, dir, point, damage, dflags, mod, + weapon, is_alternate, hit_loc, time_offset); + } + + void g_damage_stub(game::mp::gentity_s* targ, const game::mp::gentity_s* inflictor, game::mp::gentity_s* attacker, + const float* dir, const float* point, int damage, int dflags, int mod, + const unsigned int weapon, bool is_alternate, unsigned int hit_loc, + unsigned int model_index, unsigned int part_name, int time_offset) + { + if (targ->flags & 1) // godmode + { + return; + } + + if (targ->flags & 2) // demigod + { + damage = 1; + } + + g_damage_hook.invoke(targ, inflictor, attacker, dir, point, damage, dflags, mod, weapon, + is_alternate, hit_loc, model_index, part_name, time_offset); } } @@ -164,65 +225,66 @@ namespace gameplay public: void post_unpack() override { - dvars::player_sustainAmmo = dvars::register_bool("player_sustainAmmo", false, - game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo"); - pm_weapon_use_ammo_hook.create(SELECT_VALUE(0x14042E380, 0x1401F6B90), &pm_weapon_use_ammo_stub); - if (game::environment::is_sp()) { return; } -#ifdef DEBUG - // Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored' - pm_player_trace_hook.create(0x1401E8BE0, &pm_player_trace_stub); + dvars::player_sustainAmmo = dvars::register_bool("player_sustainAmmo", false, + game::DVAR_FLAG_REPLICATED, "Firing weapon will not decrease clip ammo"); + pm_weapon_use_ammo_hook.create(0x2DF830_b, &pm_weapon_use_ammo_stub); + + utils::hook::nop(0x4006AD_b, 15); + utils::hook::jump(0x4006AD_b, g_speed_stub(), true); + dvars::g_speed = dvars::register_int("g_speed", 190, 0, 1000, + game::DVAR_FLAG_REPLICATED, "changes the speed of the player"); + dvars::pm_bouncing = dvars::register_bool("pm_bouncing", false, + game::DVAR_FLAG_REPLICATED, "Enable bouncing"); + utils::hook::jump(0x2D39A4_b, pm_bouncing_stub_mp(), true); + + dvars::g_gravity = dvars::register_int("g_gravity", 800, 0, 1000, game::DVAR_FLAG_REPLICATED, + "Game gravity in inches per second squared"); + utils::hook::jump(0x3FF812_b, client_end_frame_stub(), true); + utils::hook::nop(0x3FF808_b, 1); + + /* + // Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored' + pm_player_trace_hook.create(0x2D14C0_b, &pm_player_trace_stub); // If g_enableElevators is 1 the 'ducked' flag will always be removed from the player state - utils::hook::jump(0x1401E1CD1, utils::hook::assemble(pm_trace_stub), true); - dvars::g_enableElevators = dvars::register_bool("g_enableElevators", false, game::DvarFlags::DVAR_FLAG_NONE, "Enables Elevators"); -#endif + utils::hook::jump(0x2C9F90_b, utils::hook::assemble(pm_trace_stub), true); + dvars::g_enableElevators = dvars::register_bool("g_enableElevators", false, game::DvarFlags::DVAR_FLAG_NONE, "Enables Elevators"); auto* timescale = dvars::register_float("timescale", 1.0f, 0.1f, 50.0f, game::DVAR_FLAG_REPLICATED, "Changes Timescale of the game"); - utils::hook::inject(0x1400D89A4, ×cale->current.value); - utils::hook::inject(0x1400DA9D1, ×cale->current.value); - utils::hook::inject(0x1400DB7A9, ×cale->current.value); - utils::hook::inject(0x1400DB7C6, ×cale->current.value); - utils::hook::inject(0x1400DB83C, ×cale->current.value); - utils::hook::inject(0x1400DB9CC, ×cale->current.value); - utils::hook::inject(0x1400DBAF0, ×cale->current.value); - utils::hook::inject(0x1400DBE72, ×cale->current.value); - utils::hook::inject(0x1400DBE9C, ×cale->current.value); + utils::hook::inject(0x15B204_b, ×cale->current.value); // Com_GetTimeScale + utils::hook::inject(0x17D241_b, ×cale->current.value); // Com_Restart + utils::hook::inject(0x17E609_b, ×cale->current.value); // Com_SetSlowMotion + utils::hook::inject(0x17E626_b, ×cale->current.value); // Com_SetSlowMotion + utils::hook::inject(0x17E69C_b, ×cale->current.value); // Com_SetSlowMotion + // utils::hook::inject(0x1400DB9CC, ×cale->current.value); // Com_ErrorCleanup_Shutdown (Inlined) + utils::hook::inject(0x17EAD0_b, ×cale->current.value); // Com_TimeScaleMsec (Crash) + utils::hook::inject(0x17EFE2_b, ×cale->current.value); // Com_UpdateSlowMotion + utils::hook::inject(0x17F00C_b, ×cale->current.value); // Com_UpdateSlowMotion + */ - utils::hook::call(0x1401E8830, jump_apply_slowdown_stub); + jump_apply_slowdown_hook.create(0x2BD0B0_b, jump_apply_slowdown_stub); jump_slowDownEnable = dvars::register_bool("jump_slowDownEnable", true, game::DVAR_FLAG_REPLICATED, "Slow player movement after jumping"); - utils::hook::call(0x1401E490F, pm_crashland_stub); + pm_crashland_hook.create(0x2CB070_b, pm_crashland_stub); jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED, "Enable fall damage"); dvars::g_playerEjection = dvars::register_bool("g_playerEjection", true, game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off"); - utils::hook::call(0x140323333, stuck_in_client_stub); + stuck_in_client_hook.create(0x4035F0_b, stuck_in_client_stub); - utils::hook::nop(0x140323DAD, 15); - utils::hook::jump(0x140323DAD, g_speed_stub, true); - dvars::g_speed = dvars::register_int("g_speed", 190, std::numeric_limits::min(), std::numeric_limits::max(), - game::DVAR_FLAG_REPLICATED, "changes the speed of the player"); - - // Implement player collision dvar dvars::g_playerCollision = dvars::register_bool("g_playerCollision", true, game::DVAR_FLAG_REPLICATED, "Flag whether player collision is on or off"); - utils::hook::call(0x14049D7CF, cm_transformed_capsule_trace_stub); // SV_ClipMoveToEntity - utils::hook::call(0x140240BC3, cm_transformed_capsule_trace_stub); // CG_ClipMoveToEntity + cm_transformed_capsule_trace_hook.create(0x4D63C0_b, cm_transformed_capsule_trace_stub); - // Implement bouncing dvar - dvars::pm_bouncing = dvars::register_bool("pm_bouncing", false, - game::DVAR_FLAG_REPLICATED, "Enable bouncing"); - utils::hook::jump(0x1401EAFE4, pm_bouncing_stub_mp, true); - - dvars::g_gravity = dvars::register_int("g_gravity", 800, std::numeric_limits::min(), - std::numeric_limits::max(), game::DVAR_FLAG_REPLICATED, "Game gravity in inches per second squared"); - utils::hook::jump(0x140322F72, client_end_frame_stub, true); - utils::hook::nop(0x140322F68, 1); // Nop skipped opcode + // Make noclip work + client_end_frame_hook.create(0x3FF7D0_b, client_end_frame_stub2); + g_damage_client_hook.create(0x414F10_b, g_damage_client_stub); + g_damage_hook.create(0x414A10_b, g_damage_stub); } }; } diff --git a/src/client/component/input.cpp b/src/client/component/input.cpp index 43aff87c..9e92188f 100644 --- a/src/client/component/input.cpp +++ b/src/client/component/input.cpp @@ -4,7 +4,6 @@ #include "game/game.hpp" #include "game_console.hpp" -#include "ui_scripting.hpp" #include "game/ui_scripting/execution.hpp" #include @@ -16,7 +15,6 @@ namespace input utils::hook::detour cl_char_event_hook; utils::hook::detour cl_key_event_hook; - void cl_char_event_stub(const int local_client_num, const int key) { if (ui_scripting::lui_running()) @@ -66,8 +64,8 @@ namespace input return; } - cl_char_event_hook.create(SELECT_VALUE(0x1401871A0, 0x14024E810), cl_char_event_stub); - cl_key_event_hook.create(SELECT_VALUE(0x1401874D0, 0x14024EA60), cl_key_event_stub); + cl_char_event_hook.create(SELECT_VALUE(0x1AB8F0_b, 0x12C8F0_b), cl_char_event_stub); + cl_key_event_hook.create(SELECT_VALUE(0x1ABC20_b, 0x135A70_b), cl_key_event_stub); } }; } diff --git a/src/client/component/localized_strings.cpp b/src/client/component/localized_strings.cpp index 975c76f3..1b72bea9 100644 --- a/src/client/component/localized_strings.cpp +++ b/src/client/component/localized_strings.cpp @@ -44,9 +44,9 @@ namespace localized_strings void post_unpack() override { // Change some localized strings - seh_string_ed_get_string_hook.create(SELECT_VALUE(0x1403924A0, 0x1404BB2A0), &seh_string_ed_get_string); + seh_string_ed_get_string_hook.create(SELECT_VALUE(0x3E6CE0_b, 0x585DA0_b), &seh_string_ed_get_string); } }; } -REGISTER_COMPONENT(localized_strings::component) +REGISTER_COMPONENT(localized_strings::component) \ No newline at end of file diff --git a/src/client/component/logfile.cpp b/src/client/component/logfile.cpp index 08ce07dd..2e2b3e4f 100644 --- a/src/client/component/logfile.cpp +++ b/src/client/component/logfile.cpp @@ -2,15 +2,10 @@ #include "loader/component_loader.hpp" #include "scheduler.hpp" -#include "game/scripting/entity.hpp" -#include "game/scripting/execution.hpp" -#include "game/scripting/lua/value_conversion.hpp" -#include "game/scripting/lua/error.hpp" +#include "logfile.hpp" #include -#include "logfile.hpp" - namespace logfile { std::unordered_map vm_execute_hooks; @@ -20,6 +15,9 @@ namespace logfile utils::hook::detour scr_player_killed_hook; utils::hook::detour scr_player_damage_hook; + utils::hook::detour client_command_hook; + utils::hook::detour g_shutdown_game_hook; + std::vector player_killed_callbacks; std::vector player_damage_callbacks; @@ -58,7 +56,7 @@ namespace logfile std::string convert_mod(const int meansOfDeath) { - const auto value = reinterpret_cast(0x140FEC3F0)[meansOfDeath]; + const auto value = reinterpret_cast(0x10B5290_b)[meansOfDeath]; const auto string = game::SL_ConvertToString(*value); return string; } @@ -68,7 +66,7 @@ namespace logfile const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration) { { - const std::string hitloc = reinterpret_cast(0x140FEC4D0)[hitLoc]; + const std::string hitloc = reinterpret_cast(0x10B5370_b)[hitLoc]; const auto mod_ = convert_mod(meansOfDeath); const auto weapon_ = get_weapon_name(weapon, isAlternate); @@ -110,7 +108,7 @@ namespace logfile const float* vDir, const unsigned int hitLoc, const int timeOffset) { { - const std::string hitloc = reinterpret_cast(0x140FEC4D0)[hitLoc]; + const std::string hitloc = reinterpret_cast(0x10B5370_b)[hitLoc]; const auto mod_ = convert_mod(meansOfDeath); const auto weapon_ = get_weapon_name(weapon, isAlternate); @@ -147,51 +145,6 @@ namespace logfile meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset); } - void client_command_stub(const int clientNum) - { - auto self = &game::mp::g_entities[clientNum]; - char cmd[1024] = {0}; - - game::SV_Cmd_ArgvBuffer(0, cmd, 1024); - - if (cmd == "say"s || cmd == "say_team"s) - { - auto hidden = false; - std::string message(game::ConcatArgs(1)); - - hidden = message[1] == '/'; - message.erase(0, hidden ? 2 : 1); - - scheduler::once([cmd, message, self]() - { - const scripting::entity level{*game::levelEntityId}; - const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)}; - - scripting::notify(level, cmd, {player, message}); - scripting::notify(player, cmd, {message}); - }, scheduler::pipeline::server); - - if (hidden) - { - return; - } - } - - // ClientCommand - return utils::hook::invoke(0x140336000, clientNum); - } - - void g_shutdown_game_stub(const int freeScripts) - { - { - const scripting::entity level{*game::levelEntityId}; - scripting::notify(level, "shutdownGame_called", {1}); - } - - // G_ShutdownGame - return utils::hook::invoke(0x140345A60, freeScripts); - } - unsigned int local_id_to_entity(unsigned int local_id) { const auto variable = game::scr_VarGlob->objectVariableValue[local_id]; @@ -254,7 +207,7 @@ namespace logfile a.inc(r14); a.mov(dword_ptr(rbp, 0xA4), r15d); - a.jmp(SELECT_VALUE(0x140376663, 0x140444653)); + a.jmp(SELECT_VALUE(0x3CA153_b, 0x5111B3_b)); a.bind(replace); @@ -291,25 +244,53 @@ namespace logfile hook_enabled = false; } + bool client_command_stub(const int client_num) + { + auto self = &game::mp::g_entities[client_num]; + char cmd[1024] = {0}; + + game::SV_Cmd_ArgvBuffer(0, cmd, 1024); + + if (cmd == "say"s || cmd == "say_team"s) + { + auto hidden = false; + std::string message(game::ConcatArgs(1)); + + hidden = message[1] == '/'; + message.erase(0, hidden ? 2 : 1); + + scheduler::once([cmd, message, self, hidden]() + { + const scripting::entity level{*game::levelEntityId}; + const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)}; + + scripting::notify(level, cmd, {player, message, hidden}); + scripting::notify(player, cmd, {message, hidden}); + }, scheduler::pipeline::server); + + if (hidden) + { + return false; + } + } + + return true; + } + class component final : public component_interface { public: void post_unpack() override { - utils::hook::jump(SELECT_VALUE(0x140376655, 0x140444645), utils::hook::assemble(vm_execute_stub), true); + utils::hook::jump(SELECT_VALUE(0x3CA145_b, 0x5111A5_b), utils::hook::assemble(vm_execute_stub), true); if (game::environment::is_sp()) { return; } - utils::hook::call(0x14048191D, client_command_stub); - - scr_player_damage_hook.create(0x14037DC50, scr_player_damage_stub); - scr_player_killed_hook.create(0x14037DF30, scr_player_killed_stub); - - utils::hook::call(0x140484EC0, g_shutdown_game_stub); - utils::hook::call(0x1404853C1, g_shutdown_game_stub); + scr_player_damage_hook.create(0x1CE780_b, scr_player_damage_stub); + scr_player_killed_hook.create(0x1CEA60_b, scr_player_killed_stub); } }; } diff --git a/src/client/component/logfile.hpp b/src/client/component/logfile.hpp index 77f699c8..4ab67966 100644 --- a/src/client/component/logfile.hpp +++ b/src/client/component/logfile.hpp @@ -1,5 +1,10 @@ #pragma once +#include "game/scripting/entity.hpp" +#include "game/scripting/execution.hpp" +#include "game/scripting/lua/value_conversion.hpp" +#include "game/scripting/lua/error.hpp" + namespace logfile { extern std::unordered_map vm_execute_hooks; @@ -10,4 +15,6 @@ namespace logfile void enable_vm_execute_hook(); void disable_vm_execute_hook(); + + bool client_command_stub(const int client_num); } \ No newline at end of file diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp index dc2ba9df..776313e1 100644 --- a/src/client/component/logger.cpp +++ b/src/client/component/logger.cpp @@ -110,50 +110,6 @@ namespace logger console::info(buffer); } - - void lui_error() - { - utils::hook::call(0x140162809, print_warning); - utils::hook::call(0x140162815, print_warning); - utils::hook::call(0x14016281D, print_warning); - utils::hook::call(0x140162829, print_warning); - - utils::hook::call(0x140162E32, print_warning); - utils::hook::call(0x140162E3E, print_warning); - utils::hook::call(0x140162E46, print_warning); - utils::hook::call(0x140162E52, print_warning); - - utils::hook::call(0x140168435, print_warning); - utils::hook::call(0x140168441, print_warning); - utils::hook::call(0x140168449, print_warning); - utils::hook::call(0x140168455, print_warning); - - utils::hook::call(0x14016914D, print_warning); - utils::hook::call(0x140169161, print_warning); - - utils::hook::call(0x140169C04, print_warning); - utils::hook::call(0x140169C0C, print_warning); - utils::hook::call(0x140169C18, print_warning); - - utils::hook::call(0x140169CB7, print_warning); - utils::hook::call(0x140169CDE, print_warning); - utils::hook::call(0x140169CEA, print_warning); - utils::hook::call(0x140169D03, print_warning); - - utils::hook::call(0x14016BE72, print_warning); - utils::hook::call(0x14016C020, print_warning); - } - - void lui_interface_debug_print() - { - utils::hook::call(0x14015C0B2, print_warning); - utils::hook::call(0x140162453, print_warning); - utils::hook::call(0x1401625DF, print_warning); - utils::hook::call(0x14016713C, print_dev); - utils::hook::call(0x1401687CD, print_dev); - utils::hook::call(0x14016BB8A, print_dev); - utils::hook::call(0x14016CA9C, print_dev); - } } class component final : public component_interface @@ -161,15 +117,12 @@ namespace logger public: void post_unpack() override { - if (game::environment::is_mp()) + if (!game::environment::is_dedi()) { - lui_error(); - lui_interface_debug_print(); - } - - if (!game::environment::is_sp()) - { - utils::hook::call(0x14051347F, print_com_error); + // lua stuff + utils::hook::jump(SELECT_VALUE(0x106010_b, 0x27CBB0_b), print_dev); // debug + utils::hook::jump(SELECT_VALUE(0x107680_b, 0x27E210_b), print_error); // error + utils::hook::jump(SELECT_VALUE(0x0E6E30_b, 0x1F6140_b), printf); // print } com_error_hook.create(game::Com_Error, com_error_stub); diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp index 9bc87113..53f652b4 100644 --- a/src/client/component/lui.cpp +++ b/src/client/component/lui.cpp @@ -15,14 +15,6 @@ namespace lui public: void post_unpack() override { - // Don't show create cod account popup - //utils::hook::set(0x14017C957, 0); - -//#ifdef _DEBUG - // Enable development menus (causes issues in sp) - //utils::hook::set(SELECT_VALUE(0x1400B4ABC, 0x1401AB779), 1); -//#endif - command::add("lui_open", [](const command::params& params) { if (params.size() <= 1) diff --git a/src/client/component/map_rotation.cpp b/src/client/component/map_rotation.cpp index de1f9f83..6f749304 100644 --- a/src/client/component/map_rotation.cpp +++ b/src/client/component/map_rotation.cpp @@ -9,9 +9,10 @@ namespace map_rotation { - DWORD previousPriority; namespace { + DWORD previous_priority{}; + void set_dvar(const std::string& dvar, const std::string& value) { command::execute(utils::string::va("%s \"%s\"", dvar.data(), value.data()), true); @@ -84,10 +85,10 @@ namespace map_rotation scheduler::on_game_initialized([]() { //printf("=======================setting OLD priority=======================\n"); - SetPriorityClass(GetCurrentProcess(), previousPriority); + SetPriorityClass(GetCurrentProcess(), previous_priority); }, scheduler::pipeline::main, 1s); - previousPriority = GetPriorityClass(GetCurrentProcess()); + previous_priority = GetPriorityClass(GetCurrentProcess()); //printf("=======================setting NEW priority=======================\n"); SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS); } @@ -147,7 +148,6 @@ namespace map_rotation return scheduler::cond_end; }, scheduler::pipeline::main, 1s); } - } class component final : public component_interface @@ -170,9 +170,9 @@ namespace map_rotation command::add("map_rotate", &perform_map_rotation); // Hook GScr_ExitLevel - utils::hook::jump(0x140376630, &trigger_map_rotation); // not sure if working + utils::hook::jump(0xE2670_b, &trigger_map_rotation, true); // not sure if working - previousPriority = GetPriorityClass(GetCurrentProcess()); + previous_priority = GetPriorityClass(GetCurrentProcess()); } }; } diff --git a/src/client/component/materials.cpp b/src/client/component/materials.cpp index c5fed9a2..371bea53 100644 --- a/src/client/component/materials.cpp +++ b/src/client/component/materials.cpp @@ -203,8 +203,8 @@ namespace materials } material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub); - db_material_streaming_fail_hook.create(SELECT_VALUE(0x1401D3180, 0x1402C6260), db_material_streaming_fail_stub); - db_get_material_index_hook.create(SELECT_VALUE(0x1401CAD00, 0x1402BBB20), db_get_material_index_stub); + db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub); + db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub); } }; } diff --git a/src/client/component/mods.cpp b/src/client/component/mods.cpp index ea6a9027..9e0552ee 100644 --- a/src/client/component/mods.cpp +++ b/src/client/component/mods.cpp @@ -61,7 +61,7 @@ namespace mods utils::io::create_directory("mods"); } - db_release_xassets_hook.create(SELECT_VALUE(0x1401CD560, 0x1402BF160), db_release_xassets_stub); + db_release_xassets_hook.create(SELECT_VALUE(0x1F4DB0_b, 0x399740_b), db_release_xassets_stub); command::add("loadmod", [](const command::params& params) { diff --git a/src/client/component/network.cpp b/src/client/component/network.cpp index 2a361b67..336de37f 100644 --- a/src/client/component/network.cpp +++ b/src/client/component/network.cpp @@ -60,25 +60,25 @@ namespace network // Command handled a.popad64(); a.mov(al, 1); - a.jmp(0x140252AF8); + a.jmp(0x12FCAA_b); a.bind(return_unhandled); a.popad64(); - a.jmp(0x14025234C); + a.jmp(0x12F3AC_b); } - int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2) + int net_compare_base_address(const game::netadr_s* a, const game::netadr_s* b) { - if (a1->type == a2->type) + if (a->type == b->type) { - switch (a1->type) + switch (a->type) { case game::netadrtype_t::NA_BOT: case game::netadrtype_t::NA_LOOPBACK: - return a1->port == a2->port; + return a->port == b->port; case game::netadrtype_t::NA_IP: - return !memcmp(a1->ip, a2->ip, 4); + return !memcmp(a->ip, b->ip, 4); case game::netadrtype_t::NA_BROADCAST: return true; default: @@ -89,9 +89,9 @@ namespace network return false; } - int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2) + int net_compare_address(const game::netadr_s* a, const game::netadr_s* b) { - return net_compare_base_address(a1, a2) && a1->port == a2->port; + return net_compare_base_address(a, b) && a->port == b->port; } void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*, @@ -110,17 +110,17 @@ namespace network if (net_interface && net_interface != "localhost"s) { // Sys_StringToSockaddr - utils::hook::invoke(0x1404F6580, net_interface, &address); + utils::hook::invoke(0x59E810_b, net_interface, &address); } address.sin_family = AF_INET; address.sin_port = ntohs(static_cast(port)); - const auto sock = ::socket(AF_INET, SOCK_DGRAM, protocol); + const auto sock = socket(AF_INET, SOCK_DGRAM, protocol); u_long arg = 1; ioctlsocket(sock, FIONBIO, &arg); - char optval[4] = { 1 }; + char optval[4] = {1}; setsockopt(sock, 0xFFFF, 32, optval, 4); if (bind(sock, reinterpret_cast(&address), sizeof(address)) != -1) @@ -138,10 +138,10 @@ namespace network get_callbacks()[utils::string::to_lower(command)] = callback; } - int dw_send_to_stub(const int size, const char* src, game::netadr_s* a3) + int dw_send_to_stub(const int size, const char* src, game::netadr_s* to) { sockaddr s = {}; - game::NetadrToSockadr(a3, &s); + game::NetadrToSockadr(to, &s); return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0; } @@ -229,53 +229,47 @@ namespace network } // redirect dw_sendto to raw socket - //utils::hook::jump(0x1404D850A, reinterpret_cast(0x1404D849A)); - utils::hook::call(0x140513467, dw_send_to_stub); + utils::hook::jump(0x5EEC90_b, dw_send_to_stub); utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub); // intercept command handling - utils::hook::jump(0x140252327, utils::hook::assemble(handle_command_stub), true); + utils::hook::jump(0x12F387_b, utils::hook::assemble(handle_command_stub), true); // handle xuid without secure connection - utils::hook::nop(0x140486AAF, 6); + utils::hook::nop(0x554222_b, 6); - utils::hook::jump(0x140424F20, net_compare_address); - utils::hook::jump(0x140424F70, net_compare_base_address); + utils::hook::jump(0x4F1800_b, net_compare_address); + utils::hook::jump(0x4F1850_b, net_compare_base_address); // don't establish secure conenction - utils::hook::set(0x14027EA4D, 0xEB); - utils::hook::set(0x14027EB1E, 0xEB); - utils::hook::set(0x14027EF8D, 0xEB); - utils::hook::set(0x14025081F, 0xEB); + utils::hook::set(0x358C8D_b, 0xEB); + utils::hook::set(0x358D5E_b, 0xEB); + utils::hook::set(0x3591CD_b, 0xEB); + utils::hook::set(0x12CD0F_b, 0xEB); // ignore unregistered connection - utils::hook::jump(0x140480F46, 0x140480EE5); - utils::hook::set(0x140480F3B, 0xEB); + utils::hook::jump(0x54E2D1_b, 0x54E270_b, true); + utils::hook::set(0x54E2C6_b, 0xEB); // disable xuid verification - utils::hook::set(0x14005B62D, 0xEB); - utils::hook::set(0x14005B649, 0xEB); + utils::hook::set(0x728BF_b, 0xEB); // disable xuid verification - utils::hook::nop(0x14048382C, 2); - utils::hook::set(0x140483889, 0xEB); + utils::hook::nop(0x5509D9_b, 2); + utils::hook::set(0x550A36_b, 0xEB); // ignore configstring mismatch - utils::hook::set(0x1402591C9, 0xEB); + utils::hook::set(0x341261_b, 0xEB); // ignore dw handle in SV_PacketEvent - utils::hook::set(0x1404898E2, 0xEB); - utils::hook::call(0x1404898D6, &net_compare_address); + utils::hook::set(0x1CBC22_b, 0xEB); // ignore dw handle in SV_FindClientByAddress - utils::hook::set(0x140488EFD, 0xEB); - utils::hook::call(0x140488EF1, &net_compare_address); + utils::hook::set(0x1CB24D_b, 0xEB); // ignore dw handle in SV_DirectConnect - utils::hook::set(0x140480C58, 0xEB); - utils::hook::set(0x140480E6F, 0xEB); - utils::hook::call(0x140480C4B, &net_compare_address); - utils::hook::call(0x140480E62, &net_compare_address); + utils::hook::set(0x54DFE8_b, 0xEB); + utils::hook::set(0x54E1FD_b, 0xEB); // increase cl_maxpackets dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED); @@ -284,31 +278,31 @@ namespace network dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE); // ignore impure client - utils::hook::jump(0x140481B58, 0x140481BEE); + utils::hook::jump(0x54EDD3_b, 0x54EE69_b); // don't send checksum - utils::hook::set(0x1404F6398, 0); + utils::hook::set(0x59E628_b, 0); // don't read checksum - utils::hook::set(0x1404F6620, 0xC301B0); + utils::hook::set(0x59E8B0_b, 0xC301B0); // don't try to reconnect client - utils::hook::call(0x140480DFF, reconnect_migratated_client); - utils::hook::nop(0x140480DDB, 4); // this crashes when reconnecting for some reason + utils::hook::jump(0x54D220_b, reconnect_migratated_client); + utils::hook::nop(0x54E168_b, 4); // this crashes when reconnecting for some reason // allow server owner to modify net_port before the socket bind - utils::hook::call(0x140512BE5, register_netport_stub); - utils::hook::call(0x140512D20, register_netport_stub); + utils::hook::call(0x5BD032_b, register_netport_stub); + utils::hook::call(0x5BD3F0_b, register_netport_stub); // increase allowed packet size const auto max_packet_size = 0x20000; - utils::hook::set(0x1404255F1, max_packet_size); - utils::hook::set(0x140425630, max_packet_size); - utils::hook::set(0x140425522, max_packet_size); - utils::hook::set(0x140425545, max_packet_size); + utils::hook::set(0x4F1ED1_b, max_packet_size); + utils::hook::set(0x4F1F10_b, max_packet_size); + utils::hook::set(0x4F1E02_b, max_packet_size); + utils::hook::set(0x4F1E25_b, max_packet_size); // ignore built in "print" oob command and add in our own - utils::hook::set(0x14025280E, 0xEB); + utils::hook::set(0x12F817_b, 0xEB); on("print", [](const game::netadr_s&, const std::string_view& data) { const std::string message{data}; @@ -317,7 +311,7 @@ namespace network // Use our own socket since the game's socket doesn't work with non localhost addresses // why? no idea - utils::hook::jump(0x140512B40, create_socket); + utils::hook::jump(0x5BD210_b, create_socket); } } }; diff --git a/src/client/component/party.cpp b/src/client/component/party.cpp index 5a88eb65..da192a27 100644 --- a/src/client/component/party.cpp +++ b/src/client/component/party.cpp @@ -71,11 +71,11 @@ namespace party perform_game_initialization(); // exit from virtuallobby - utils::hook::invoke(0x140256D40, 1); + utils::hook::invoke(0x13C9C0_b, 1); // CL_ConnectFromParty char session_info[0x100] = {}; - utils::hook::invoke(0x140251560, 0, session_info, &target, mapname.data(), gametype.data()); + utils::hook::invoke(0x12DFF0_b, 0, session_info, &target, mapname.data(), gametype.data()); } std::string get_dvar_string(const std::string& dvar) @@ -111,52 +111,43 @@ namespace party return false; } - void didyouknow_stub(const char* dvar_name, const char* string) + const char* get_didyouknow_stub(void* table, int row, int column) { - if (!party::sv_motd.empty()) + if (party::sv_motd.empty()) { - string = party::sv_motd.data(); + return utils::hook::invoke(0x5A0AC0_b, table, row, column); } - - // This function either does Dvar_SetString or Dvar_RegisterString for the given dvar - utils::hook::invoke(0x1404FB210, dvar_name, string); + return utils::string::va("%s", party::sv_motd.data()); } - void disconnect_stub() + void disconnect() { if (!game::VirtualLobby_Loaded()) { if (game::CL_IsCgameInitialized()) { - // CL_ForwardCommandToServer - utils::hook::invoke(0x140253480, 0, "disconnect"); + // CL_AddReliableCommand + utils::hook::invoke(0x12B810_b, 0, "disconnect"); // CL_WritePacket - utils::hook::invoke(0x14024DB10, 0); + utils::hook::invoke(0x13D490_b, 0); } // CL_Disconnect - utils::hook::invoke(0x140252060, 0); + utils::hook::invoke(0x12F080_b, 0); } } - utils::hook::detour cldisconnect_hook; + utils::hook::detour cl_disconnect_hook; - void cl_disconnect_stub(int a1) + void cl_disconnect_stub(int showMainMenu) // possibly bool { party::clear_sv_motd(); - cldisconnect_hook.invoke(a1); + cl_disconnect_hook.invoke(showMainMenu); } - const auto drop_reason_stub = utils::hook::assemble([](utils::hook::assembler& a) - { - a.mov(rdx, rdi); - a.mov(ecx, 2); - a.jmp(0x140251F78); - }); - void menu_error(const std::string& error) { - utils::hook::invoke(0x1400DACC0, error.data(), "MENU_NOTICE"); - utils::hook::set(0x142C1DA98, 1); + utils::hook::invoke(0x17D770_b, error.data(), "MENU_NOTICE"); + utils::hook::set(0x2ED2F78_b, 1); } } @@ -192,9 +183,15 @@ namespace party int get_client_count() { auto count = 0; + const auto* svs_clients = *game::mp::svs_clients; + if (svs_clients == nullptr) + { + return count; + } + for (auto i = 0; i < *game::mp::svs_numclients; ++i) { - if (game::mp::svs_clients[i].header.state >= 1) + if (svs_clients[i].header.state >= 1) { ++count; } @@ -206,9 +203,15 @@ namespace party int get_bot_count() { auto count = 0; + const auto* svs_clients = *game::mp::svs_clients; + if (svs_clients == nullptr) + { + return count; + } + for (auto i = 0; i < *game::mp::svs_numclients; ++i) { - if (game::mp::svs_clients[i].header.state >= 1 && + if (svs_clients[i].header.state >= 1 && game::SV_BotIsBot(i)) { ++count; @@ -282,7 +285,6 @@ namespace party } console::info("Starting map: %s\n", mapname.data()); - auto* gametype = game::Dvar_FindVar("g_gametype"); if (gametype && gametype->current.string) { @@ -317,21 +319,28 @@ namespace party return; } - // hook disconnect command function - utils::hook::jump(0x1402521C7, disconnect_stub); - // detour CL_Disconnect to clear motd - cldisconnect_hook.create(0x140252060, cl_disconnect_stub); + cl_disconnect_hook.create(0x12F080_b, cl_disconnect_stub); if (game::environment::is_mp()) { // show custom drop reason - utils::hook::nop(0x140251EFB, 13); - utils::hook::jump(0x140251EFB, drop_reason_stub, true); + utils::hook::nop(0x12EF4E_b, 13); + utils::hook::jump(0x12EF4E_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.mov(rdx, rsi); + a.mov(ecx, 2); + a.jmp(0x12EF27_b); + }), true); + + command::add("disconnect", disconnect); } // enable custom kick reason in GScr_KickPlayer - utils::hook::set(0x140376A1D, 0xEB); + utils::hook::set(0xE423D_b, 0xEB); + + // allow custom didyouknow based on sv_motd + utils::hook::call(0x1A8A3A_b, get_didyouknow_stub); command::add("map", [](const command::params& argument) { @@ -349,11 +358,12 @@ namespace party { return; } - *reinterpret_cast(0x14A3A91D0) = 1; // sv_map_restart - *reinterpret_cast(0x14A3A91D4) = 1; // sv_loadScripts - *reinterpret_cast(0x14A3A91D8) = 0; // sv_migrate - utils::hook::invoke(0x14047E7F0); // SV_CheckLoadGame + *reinterpret_cast(0xB7B8E60_b) = 1; // sv_map_restart + *reinterpret_cast(0xB7B8E64_b) = 1; // sv_loadScripts + *reinterpret_cast(0xB7B8E68_b) = 0; // sv_migrate + + utils::hook::invoke(0x54BD50_b); // SV_CheckLoadGame }); command::add("fast_restart", []() @@ -546,8 +556,6 @@ namespace party printf("%s\n", message.data()); }); - utils::hook::call(0x1404C6E8D, didyouknow_stub); // allow custom didyouknow based on sv_motd - network::on("getInfo", [](const game::netadr_s& target, const std::string_view& data) { utils::info_string info{}; diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 179cae0f..276841bf 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -46,7 +46,7 @@ namespace patches return "Unknown Soldier"; } - return std::string{ username, username_len - 1 }; + return std::string{username, username_len - 1}; } utils::hook::detour com_register_dvars_hook; @@ -57,23 +57,21 @@ namespace patches { // Make name save dvars::register_string("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name."); - - // Disable data validation error popup - dvars::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, ""); } return com_register_dvars_hook.invoke(); } - void set_client_dvar_from_server_stub(void* a1, void* a2, const char* dvar, const char* value) + utils::hook::detour set_client_dvar_from_server_hook; + + void set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar, const char* value) { if (dvar == "cg_fov"s || dvar == "cg_fovMin"s) { return; } - // CG_SetClientDvarFromServer - utils::hook::invoke(0x140236120, a1, a2, dvar, value); + set_client_dvar_from_server_hook.invoke(0x11AA90_b, clientNum, cgameGlob, dvar, value); } const char* db_read_raw_file_stub(const char* filename, char* buf, const int size) @@ -92,7 +90,7 @@ namespace patches } // DB_ReadRawFile - return utils::hook::invoke(SELECT_VALUE(0x1401CD4F0, 0x1402BEF10), filename, buf, size); + return utils::hook::invoke(SELECT_VALUE(0x1F4D00_b, 0x3994B0_b), filename, buf, size); } void bsp_sys_error_stub(const char* error, const char* arg1) @@ -114,9 +112,15 @@ namespace patches utils::hook::detour cmd_lui_notify_server_hook; void cmd_lui_notify_server_stub(game::mp::gentity_s* ent) { + const auto svs_clients = *game::mp::svs_clients; + if (svs_clients == nullptr) + { + return; + } + command::params_sv params{}; const auto menu_id = atoi(params.get(1)); - const auto client = &game::mp::svs_clients[ent->s.entityNum]; + const auto client = &svs_clients[ent->s.entityNum]; // 22 => "end_game" if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK) @@ -138,15 +142,17 @@ namespace patches return; } - utils::hook::invoke(0x140481A00, client, msg); + utils::hook::invoke(0x54EC50_b, client, msg); } - void aim_assist_add_to_target_list(void* a1, void* a2) + void aim_assist_add_to_target_list(void* aaGlob, void* screenTarget) { if (!dvars::aimassist_enabled->current.enabled) + { return; + } - game::AimAssist_AddToTargetList(a1, a2); + game::AimAssist_AddToTargetList(aaGlob, screenTarget); } } @@ -156,15 +162,15 @@ namespace patches void post_unpack() override { // Register dvars - com_register_dvars_hook.create(SELECT_VALUE(0x140351B80, 0x1400D9320), &com_register_dvars_stub); + com_register_dvars_hook.create(SELECT_VALUE(0x385BE0_b, 0x15BB60_b), &com_register_dvars_stub); // Unlock fps in main menu - utils::hook::set(SELECT_VALUE(0x14018D47B, 0x14025B86B), 0xEB); + utils::hook::set(SELECT_VALUE(0x1B1EAB_b, 0x34396B_b), 0xEB); if (!game::environment::is_dedi()) { // Fix mouse lag - utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6); + utils::hook::nop(SELECT_VALUE(0x4631F9_b, 0x5BFF89_b), 6); scheduler::loop([]() { SetThreadExecutionState(ES_DISPLAY_REQUIRED); @@ -177,11 +183,11 @@ namespace patches dvars::override::register_float("cg_fovMin", 1.f, 1.0f, 90.f, game::DvarFlags::DVAR_FLAG_SAVED); // Allow kbam input when gamepad is enabled - utils::hook::nop(SELECT_VALUE(0x14018797E, 0x14024EF60), 2); - utils::hook::nop(SELECT_VALUE(0x1401856DC, 0x14024C6B0), 6); + utils::hook::nop(SELECT_VALUE(0x1AC0CE_b, 0x135EFB_b), 2); + utils::hook::nop(SELECT_VALUE(0x1A9DDC_b, 0x13388F_b), 6); // Allow executing custom cfg files with the "exec" command - utils::hook::call(SELECT_VALUE(0x140343855, 0x140403E28), db_read_raw_file_stub); + utils::hook::call(SELECT_VALUE(0x376EB5_b, 0x156D41_b), db_read_raw_file_stub); if (!game::environment::is_sp()) { @@ -191,56 +197,54 @@ namespace patches static void patch_mp() { - // Use name dvar - utils::hook::jump(0x14050FF90, &live_get_local_client_name); + utils::hook::jump(0x5BB9C0_b, &live_get_local_client_name); + + // Disable data validation error popup + dvars::override::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE); // Patch SV_KickClientNum - sv_kick_client_num_hook.create(0x14047ED00, &sv_kick_client_num); + sv_kick_client_num_hook.create(game::SV_KickClientNum, &sv_kick_client_num); // block changing name in-game - utils::hook::set(0x14047FC90, 0xC3); - - // patch "Couldn't find the bsp for this map." error to not be fatal in mp - utils::hook::call(0x1402BA26B, bsp_sys_error_stub); + utils::hook::set(0x54CFF0_b, 0xC3); // client side aim assist dvar dvars::aimassist_enabled = dvars::register_bool("aimassist_enabled", true, game::DvarFlags::DVAR_FLAG_SAVED, "Enables aim assist for controllers"); - utils::hook::call(0x14009EE9E, aim_assist_add_to_target_list); + utils::hook::call(0xE857F_b, aim_assist_add_to_target_list); + + // patch "Couldn't find the bsp for this map." error to not be fatal in mp + utils::hook::call(0x39465B_b, bsp_sys_error_stub); // isProfanity - utils::hook::set(0x1402877D0, 0xC3C033); - - // disable emblems - dvars::override::register_int("emblems_active", 0, 0, 0, game::DVAR_FLAG_NONE); - utils::hook::set(0x140479590, 0xC3); // don't register commands + utils::hook::set(0x361AA0_b, 0xC3C033); // disable elite_clan dvars::override::register_int("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE); - utils::hook::set(0x140585680, 0xC3); // don't register commands + utils::hook::set(0x62D2F0_b, 0xC3); // don't register commands // disable codPointStore dvars::override::register_int("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE); // don't register every replicated dvar as a network dvar - utils::hook::nop(0x14039E58E, 5); // dvar_foreach + utils::hook::nop(0x47408E_b, 5); // dvar_foreach // patch "Server is different version" to show the server client version - utils::hook::inject(0x140480955, VERSION); + utils::hook::inject(0x54DCE5_b, VERSION); // prevent servers overriding our fov - utils::hook::call(0x14023279E, set_client_dvar_from_server_stub); - utils::hook::nop(0x1400DAF69, 5); - utils::hook::nop(0x140190C16, 5); - utils::hook::set(0x14021D22A, 0xEB); + set_client_dvar_from_server_hook.create(0x11AA90_b, set_client_dvar_from_server_stub); + utils::hook::nop(0x17DA96_b, 0x16); + utils::hook::nop(0xE00BE_b, 0x17); + utils::hook::set(0x307F39_b, 0xEB); - // some anti tamper thing that kills performance + // some [data validation] anti tamper thing that kills performance dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ); // unlock safeArea_* - utils::hook::jump(0x1402624F5, 0x140262503); - utils::hook::jump(0x14026251C, 0x140262547); + utils::hook::jump(0x347BC5_b, 0x347BD3_b); + utils::hook::jump(0x347BEC_b, 0x347C17_b); dvars::override::register_float("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED); dvars::override::register_float("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED); @@ -262,23 +266,26 @@ namespace patches dvars::override::register_int("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED); // Prevent clients from ending the game as non host by sending 'end_game' lui notification - cmd_lui_notify_server_hook.create(0x140335A70, cmd_lui_notify_server_stub); + cmd_lui_notify_server_hook.create(0x412D50_b, cmd_lui_notify_server_stub); // Prevent clients from sending invalid reliableAcknowledge - utils::hook::call(0x1404899C6, sv_execute_client_message_stub); + utils::hook::call(0x1CBD06_b, sv_execute_client_message_stub); // "fix" for rare 'Out of memory error' error if (utils::flags::has_flag("memoryfix")) { - utils::hook::jump(0x140578BE0, malloc); - utils::hook::jump(0x140578B00, _aligned_malloc); - utils::hook::jump(0x140578C40, free); - utils::hook::jump(0x140578D30, realloc); - utils::hook::jump(0x140578B60, _aligned_realloc); + utils::hook::jump(0x6200C0_b, malloc); + utils::hook::jump(0x61FFE0_b, _aligned_malloc); + utils::hook::jump(0x620120_b, free); + utils::hook::jump(0x620210_b, realloc); + utils::hook::jump(0x620040_b, _aligned_realloc); } // Change default hostname and make it replicated dvars::override::register_string("sv_hostname", "^2H1-Mod^7 Default Server", game::DVAR_FLAG_REPLICATED); + + // Dont free server/client memory on asset loading (fixes crashing on map rotation) + utils::hook::nop(0x132474_b, 5); } }; } diff --git a/src/client/component/ranked.cpp b/src/client/component/ranked.cpp index f4776e2d..955a1984 100644 --- a/src/client/component/ranked.cpp +++ b/src/client/component/ranked.cpp @@ -27,19 +27,17 @@ namespace ranked dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED); } - if (game::environment::is_dedi() && !utils::flags::has_flag("unranked")) + if (game::environment::is_dedi()) { dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE); - - // Skip some check in _menus.gsc - dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, ""); + dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, ""); // Skip some check in _menus.gsc } // Always run bots, even if xblive_privatematch is 0 - utils::hook::set(0x1401D9300, 0xC301B0); // BG_BotSystemEnabled - utils::hook::set(0x1401D90D0, 0xC301B0); // BG_AISystemEnabled - utils::hook::set(0x1401D92A0, 0xC301B0); // BG_BotFastFileEnabled - utils::hook::set(0x1401D9400, 0xC301B0); // BG_BotsUsingTeamDifficulty + utils::hook::set(0x2C10B0_b, 0xC301B0); // BG_BotSystemEnabled + utils::hook::set(0x2C0E60_b, 0xC301B0); // BG_AISystemEnabled + utils::hook::set(0x2C1040_b, 0xC301B0); // BG_BotFastFileEnabled + utils::hook::set(0x2C11B0_b, 0xC301B0); // BG_BotsUsingTeamDifficulty } }; } diff --git a/src/client/component/rcon.cpp b/src/client/component/rcon.cpp new file mode 100644 index 00000000..a7ea7705 --- /dev/null +++ b/src/client/component/rcon.cpp @@ -0,0 +1,232 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include "command.hpp" +#include "console.hpp" +#include "network.hpp" +#include "scheduler.hpp" +#include "rcon.hpp" + +#include +#include + +namespace rcon +{ + namespace + { + bool is_redirecting_ = false; + bool has_redirected_ = false; + game::netadr_s redirect_target_ = {}; + std::recursive_mutex redirect_lock; + + void setup_redirect(const game::netadr_s& target) + { + std::lock_guard $(redirect_lock); + + has_redirected_ = false; + is_redirecting_ = true; + redirect_target_ = target; + } + + void clear_redirect() + { + std::lock_guard $(redirect_lock); + + has_redirected_ = false; + is_redirecting_ = false; + redirect_target_ = {}; + } + + void send_rcon_command(const std::string& password, const std::string& data) + { + // If you are the server, don't bother with rcon and just execute the command + if (game::Dvar_FindVar("sv_running")->current.enabled) + { + game::Cbuf_AddText(0, 0, data.data()); + return; + } + + if (password.empty()) + { + console::info("You must login first to use RCON\n"); + return; + } + + if (*game::mp::connect_state != nullptr && *game::connectionState >= game::CA_CONNECTED) + { + const auto target = (*game::mp::connect_state)->address; + const auto buffer = password + " " + data; + network::send(target, "rcon", buffer); + } + else + { + console::warn("You need to be connected to a server!\n"); + } + } + + std::string build_status_buffer() + { + const auto sv_maxclients = game::Dvar_FindVar("sv_maxclients"); + const auto mapname = game::Dvar_FindVar("mapname"); + + std::string buffer{}; + buffer.append(utils::string::va("map: %s\n", mapname->current.string)); + buffer.append( + "num score bot ping guid name address qport\n"); + buffer.append( + "--- ----- --- ---- -------------------------------- ---------------- --------------------- -----\n"); + + const auto svs_clients = *game::mp::svs_clients; + if (svs_clients == nullptr) + { + return buffer; + } + + for (int i = 0; i < sv_maxclients->current.integer; i++) + { + const auto client = &svs_clients[i]; + + char clean_name[32] = {0}; + strncpy_s(clean_name, client->name, sizeof(clean_name)); + game::I_CleanStr(clean_name); + + if (client->header.state >= 1) + { + 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; + } + } + + bool message_redirect(const std::string& message) + { + std::lock_guard $(redirect_lock); + + if (is_redirecting_) + { + has_redirected_ = true; + network::send(redirect_target_, "print", message, '\n'); + return true; + } + return false; + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + if (game::environment::is_sp()) + { + return; + } + + scheduler::once([]() + { + dvars::register_string("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE, + "The password for remote console"); + }, scheduler::pipeline::main); + + command::add("status", []() + { + const auto sv_running = game::Dvar_FindVar("sv_running"); + if (game::VirtualLobby_Loaded() || !sv_running || !sv_running->current.enabled) + { + console::error("Server is not running\n"); + return; + } + + auto status_buffer = build_status_buffer(); + console::info(status_buffer.data()); + }); + + if (!game::environment::is_dedi()) + { + command::add("rcon", [&](const command::params& params) + { + static std::string rcon_password{}; + + if (params.size() < 2) return; + + const auto operation = params.get(1); + if (operation == "login"s) + { + if (params.size() < 3) return; + + rcon_password = params.get(2); + } + else if (operation == "logout"s) + { + rcon_password.clear(); + } + else + { + send_rcon_command(rcon_password, params.join(1)); + } + }); + } + else + { + network::on("rcon", [](const game::netadr_s& addr, const std::string_view& data) + { + const auto message = std::string{data}; + const auto pos = message.find_first_of(" "); + if (pos == std::string::npos) + { + network::send(addr, "print", "Invalid RCon request", '\n'); + console::info("Invalid RCon request from %s\n", network::net_adr_to_string(addr)); + return; + } + + const auto password = message.substr(0, pos); + const auto command = message.substr(pos + 1); + const auto rcon_password = game::Dvar_FindVar("rcon_password"); + if (command.empty() || !rcon_password || !rcon_password->current.string || !strlen( + rcon_password->current.string)) + { + return; + } + + setup_redirect(addr); + + if (password != rcon_password->current.string) + { + network::send(redirect_target_, "print", "Invalid rcon password", '\n'); + console::error("Invalid rcon password\n"); + } + else + { + command::execute(command, true); + } + + if (!has_redirected_) + { + network::send(redirect_target_, "print", "", '\n'); + } + + clear_redirect(); + }); + } + } + }; +} + +REGISTER_COMPONENT(rcon::component) diff --git a/src/client/component/rcon.hpp b/src/client/component/rcon.hpp new file mode 100644 index 00000000..2a6c23c8 --- /dev/null +++ b/src/client/component/rcon.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace rcon +{ + bool message_redirect(const std::string& message); +} diff --git a/src/client/component/redirect.cpp b/src/client/component/redirect.cpp index 0443df55..e44be742 100644 --- a/src/client/component/redirect.cpp +++ b/src/client/component/redirect.cpp @@ -1,8 +1,10 @@ #include #include "loader/component_loader.hpp" + +#include "game_module.hpp" + #include #include -#include "game_module.hpp" namespace redirect { diff --git a/src/client/component/renderer.cpp b/src/client/component/renderer.cpp index fea92f58..eed4cd4e 100644 --- a/src/client/component/renderer.cpp +++ b/src/client/component/renderer.cpp @@ -1,5 +1,8 @@ #include #include "loader/component_loader.hpp" + +#include "dvars.hpp" + #include "game/game.hpp" #include "game/dvars.hpp" @@ -30,7 +33,6 @@ namespace renderer void gfxdrawmethod() { game::gfxDrawMethod->drawScene = game::GFX_DRAW_SCENE_STANDARD; - game::gfxDrawMethod->baseTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_LIT; game::gfxDrawMethod->emissiveTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_EMISSIVE; game::gfxDrawMethod->forceTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : 242; @@ -67,19 +69,15 @@ namespace renderer dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 4, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting"); - r_init_draw_method_hook.create(SELECT_VALUE(0x1404BD140, 0x1405C46E0), &r_init_draw_method_stub); - r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404F8870, 0x1405FF9E0), &r_update_front_end_dvar_options_stub); + r_init_draw_method_hook.create(SELECT_VALUE(0x5467E0_b, 0x669580_b), &r_init_draw_method_stub); + r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x583560_b, 0x6A78C0_b), &r_update_front_end_dvar_options_stub); - // use "saved" flags for "r_normalMap" - utils::hook::set(SELECT_VALUE(0x1404CF5CA, 0x1405D460E), game::DVAR_FLAG_SAVED); - - // use "saved" flags for "r_specularMap" - utils::hook::set(SELECT_VALUE(0x1404CF5F5, 0x1405D4639), game::DVAR_FLAG_SAVED); - - // use "saved" flags for "r_specOccMap" - utils::hook::set(SELECT_VALUE(0x1404CF620, 0x1405D4664), game::DVAR_FLAG_SAVED); + // use "saved" flags + dvars::override::register_enum("r_normalMap", game::DVAR_FLAG_SAVED); + dvars::override::register_enum("r_specularMap", game::DVAR_FLAG_SAVED); + dvars::override::register_enum("r_specOccMap", game::DVAR_FLAG_SAVED); } }; } -REGISTER_COMPONENT(renderer::component) \ No newline at end of file +REGISTER_COMPONENT(renderer::component) diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp index 68d82dd1..ac97bec6 100644 --- a/src/client/component/scheduler.cpp +++ b/src/client/component/scheduler.cpp @@ -75,7 +75,8 @@ namespace scheduler { new_callbacks_.access([&](task_list& new_tasks) { - tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), std::move_iterator(new_tasks.end())); + tasks.insert(tasks.end(), std::move_iterator(new_tasks.begin()), + std::move_iterator(new_tasks.end())); new_tasks = {}; }); }); @@ -99,7 +100,6 @@ namespace scheduler void r_end_frame_stub() { execute(pipeline::renderer); - r_end_frame_hook.invoke(); } void server_frame_stub() @@ -108,10 +108,14 @@ namespace scheduler execute(pipeline::server); } - void main_frame_stub() + void* main_frame_stub() { - main_frame_hook.invoke(); - execute(pipeline::main); + const auto _0 = gsl::finally([]() + { + execute(pipeline::main); + }); + + return main_frame_hook.invoke(); } void hks_frame_stub() @@ -121,11 +125,12 @@ namespace scheduler { execute(pipeline::lui); } + hks_frame_hook.invoke(); } } void schedule(const std::function& callback, const pipeline type, - const std::chrono::milliseconds delay) + const std::chrono::milliseconds delay) { assert(type >= 0 && type < pipeline::count); @@ -138,7 +143,7 @@ namespace scheduler } void loop(const std::function& callback, const pipeline type, - const std::chrono::milliseconds delay) + const std::chrono::milliseconds delay) { schedule([callback]() { @@ -148,7 +153,7 @@ namespace scheduler } void once(const std::function& callback, const pipeline type, - const std::chrono::milliseconds delay) + const std::chrono::milliseconds delay) { schedule([callback]() { @@ -158,7 +163,7 @@ namespace scheduler } void on_game_initialized(const std::function& callback, const pipeline type, - const std::chrono::milliseconds delay) + const std::chrono::milliseconds delay) { schedule([=]() { @@ -190,10 +195,22 @@ namespace scheduler void post_unpack() override { - r_end_frame_hook.create(SELECT_VALUE(0x1404F7310, 0x1405FE470), scheduler::r_end_frame_stub); - g_run_frame_hook.create(SELECT_VALUE(0x1402772D0, 0x14033A640), scheduler::server_frame_stub); - main_frame_hook.create(SELECT_VALUE(0x1401CE8D0, 0x1400D8310), scheduler::main_frame_stub); - hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), scheduler::hks_frame_stub); + utils::hook::jump(SELECT_VALUE(0x581FB0_b, 0x6A6300_b), utils::hook::assemble([](utils::hook::assembler& a) + { + a.pushad64(); + a.call_aligned(r_end_frame_stub); + a.popad64(); + + a.sub(rsp, 0x28); + a.call(SELECT_VALUE(0x581840_b, 0x6A5C20_b)); + a.mov(rax, SELECT_VALUE(0x1182A680_b, 0xEAB4308_b)); + a.mov(rax, qword_ptr(rax)); + a.jmp(SELECT_VALUE(0x581FC0_b, 0x6A6310_b)); + }), true); + + g_run_frame_hook.create(SELECT_VALUE(0x2992E0_b, 0x417940_b), scheduler::server_frame_stub); + main_frame_hook.create(SELECT_VALUE(0x1B1DF0_b, 0x3438B0_b), scheduler::main_frame_stub); + hks_frame_hook.create(SELECT_VALUE(0x1028D0_b, 0x2792E0_b), scheduler::hks_frame_stub); } void pre_destroy() override @@ -207,4 +224,4 @@ namespace scheduler }; } -REGISTER_COMPONENT(scheduler::component) +REGISTER_COMPONENT(scheduler::component) \ No newline at end of file diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp index b31e2329..efb81f41 100644 --- a/src/client/component/scripting.cpp +++ b/src/client/component/scripting.cpp @@ -94,6 +94,7 @@ namespace scripting script_function_table.clear(); } + scripting::notify(*game::levelEntityId, "shutdownGame_called", {1}); lua::engine::stop(); return g_shutdown_game_hook.invoke(free_scripts); } @@ -159,26 +160,6 @@ namespace scripting scripting::token_map[str] = result; return result; } - - game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default) - { - const auto result = db_find_xasset_header_hook.invoke(type, name, allow_create_default); - if (!g_dump_scripts->current.enabled || type != game::XAssetType::ASSET_TYPE_SCRIPTFILE) - { - return result; - } - - std::string buffer; - buffer.append(result.scriptfile->name, strlen(result.scriptfile->name) + 1); - buffer.append(reinterpret_cast(&result.scriptfile->compressedLen), 4); - buffer.append(reinterpret_cast(&result.scriptfile->len), 4); - buffer.append(reinterpret_cast(&result.scriptfile->bytecodeLen), 4); - buffer.append(result.scriptfile->buffer, result.scriptfile->compressedLen); - buffer.append(result.scriptfile->bytecode, result.scriptfile->bytecodeLen); - utils::io::write_file(utils::string::va("gsc_dump/%s.gscbin", name), buffer); - - return result; - } } class component final : public component_interface @@ -186,27 +167,24 @@ namespace scripting public: void post_unpack() override { - vm_notify_hook.create(SELECT_VALUE(0x140379A00, 0x1404479F0), vm_notify_stub); + vm_notify_hook.create(SELECT_VALUE(0x3CD500_b, 0x514560_b), vm_notify_stub); - scr_add_class_field_hook.create(SELECT_VALUE(0x140370370, 0x14043E2C0), scr_add_class_field_stub); + scr_add_class_field_hook.create(SELECT_VALUE(0x3C3CE0_b, 0x50AE20_b), scr_add_class_field_stub); - scr_set_thread_position_hook.create(SELECT_VALUE(0x14036A180, 0x140437D10), scr_set_thread_position_stub); - process_script_hook.create(SELECT_VALUE(0x1403737E0, 0x1404417E0), process_script_stub); + scr_set_thread_position_hook.create(SELECT_VALUE(0x3BD890_b, 0x504870_b), scr_set_thread_position_stub); + process_script_hook.create(SELECT_VALUE(0x3C7200_b, 0x50E340_b), process_script_stub); sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub); if (!game::environment::is_sp()) { - scr_load_level_hook.create(SELECT_VALUE(0x1402A5BE0, 0x1403727C0), scr_load_level_stub); + scr_load_level_hook.create(0x450FC0_b, scr_load_level_stub); } else { - vm_execute_hook.create(SELECT_VALUE(0x140376590, 0x140444580), vm_execute_stub); + vm_execute_hook.create(0x3CA080_b, vm_execute_stub); } - g_shutdown_game_hook.create(SELECT_VALUE(0x140277D40, 0x140345A60), g_shutdown_game_stub); - - db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub); - g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts"); + g_shutdown_game_hook.create(SELECT_VALUE(0x2A5130_b, 0x422F30_b), g_shutdown_game_stub); scheduler::loop([]() { diff --git a/src/client/component/server_list.cpp b/src/client/component/server_list.cpp index bbe3cb0d..54354f7f 100644 --- a/src/client/component/server_list.cpp +++ b/src/client/component/server_list.cpp @@ -118,8 +118,7 @@ namespace server_list return diff > server_limit ? server_limit : static_cast(diff); } - const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const int index, - const int column) + const char* ui_feeder_item_text(const int index, const int column) { std::lock_guard _(mutex); @@ -260,18 +259,18 @@ namespace server_list utils::hook::detour lui_open_menu_hook; - void lui_open_menu_stub(int controllerIndex, const char* menu, int a3, int a4, unsigned int a5) + void lui_open_menu_stub(int controllerIndex, const char* menuName, int isPopup, int isModal, unsigned int isExclusive) { #ifdef DEBUG - console::info("[LUI] %s\n", menu); + console::info("[LUI] %s\n", menuName); #endif - if (!strcmp(menu, "menu_systemlink_join")) + if (!strcmp(menuName, "menu_systemlink_join")) { refresh_server_list(); } - lui_open_menu_hook.invoke(controllerIndex, menu, a3, a4, a5); + lui_open_menu_hook.invoke(controllerIndex, menuName, isPopup, isModal, isExclusive); } } @@ -321,6 +320,11 @@ namespace server_list return; } + if (info.get("gamename") != "H1") + { + return; + } + int start_time{}; const auto now = game::Sys_Milliseconds(); @@ -366,13 +370,64 @@ namespace server_list lui_open_menu_hook.create(game::LUI_OpenMenu, lui_open_menu_stub); // replace UI_RunMenuScript call in LUI_CoD_LuaCall_RefreshServerList to our refresh_servers - utils::hook::call(0x14018A0C9, &refresh_server_list); - utils::hook::call(0x14018A5DE, &join_server); - utils::hook::nop(0x14018A5FD, 5); + utils::hook::jump(0x28E049_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.pushad64(); + a.call_aligned(refresh_server_list); + a.popad64(); + + a.xor_(eax, eax); + a.mov(rbx, qword_ptr(rsp, 0x38)); + a.add(rsp, 0x20); + a.pop(rdi); + a.ret(); + }), true); + + utils::hook::jump(0x28E557_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.mov(r8d, edi); + a.mov(ecx, eax); + a.mov(ebx, eax); + + a.pushad64(); + a.call_aligned(join_server); + a.popad64(); + + a.jmp(0x28E563_b); + }), true); + + utils::hook::nop(0x28E57D_b, 5); // do feeder stuff - utils::hook::call(0x14018A199, &ui_feeder_count); - utils::hook::call(0x14018A3B1, &ui_feeder_item_text); + utils::hook::jump(0x28E117_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.mov(ecx, eax); + + a.pushad64(); + a.call_aligned(ui_feeder_count); + a.movd(xmm0, eax); + a.popad64(); + + a.mov(rax, qword_ptr(rbx, 0x48)); + a.cvtdq2ps(xmm0, xmm0); + a.jmp(0x28E12B_b); + }), true); + + utils::hook::jump(0x28E331_b, utils::hook::assemble([](utils::hook::assembler& a) + { + a.push(rax); + a.pushad64(); + a.mov(rcx, r9); // index + a.mov(rdx, qword_ptr(rsp, 0x88 + 0x20)); // column + a.call_aligned(ui_feeder_item_text); + a.mov(qword_ptr(rsp, 0x80), rax); + a.popad64(); + a.pop(rax); + + a.mov(rsi, qword_ptr(rsp, 0x90)); + a.mov(rdi, rax); + a.jmp(0x28E341_b); + }), true); scheduler::loop(do_frame_work, scheduler::pipeline::main); diff --git a/src/client/component/shaders.cpp b/src/client/component/shaders.cpp deleted file mode 100644 index 11e2e0d6..00000000 --- a/src/client/component/shaders.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include "loader/component_loader.hpp" - -#include "scheduler.hpp" -#include "dvars.hpp" - -#include "game/game.hpp" -#include "game/dvars.hpp" - -#include -#include -#include - -namespace shaders -{ - namespace - { - game::dvar_t* disable_shader_caching = nullptr; - - bool shader_should_show_dialog_stub() - { - return !disable_shader_caching->current.enabled; - } - } - - class component final : public component_interface - { - public: - void post_unpack() override - { - if (!game::environment::is_mp()) - { - return; - } - - const auto has_flag = utils::flags::has_flag("noshadercaching"); - - disable_shader_caching = dvars::register_bool("disable_shader_caching", has_flag, - game::DVAR_FLAG_SAVED, "Disable shader caching"); - if (has_flag) - { - dvars::override::set_bool("disable_shader_caching", 1); - dvars::override::set_from_string("disable_shader_caching", "1"); - } - - utils::hook::jump(0x14007E710, shader_should_show_dialog_stub); - } - }; -} - -REGISTER_COMPONENT(shaders::component) diff --git a/src/client/component/slowmotion.cpp b/src/client/component/slowmotion.cpp index f75f8a7e..f7ed223a 100644 --- a/src/client/component/slowmotion.cpp +++ b/src/client/component/slowmotion.cpp @@ -45,7 +45,7 @@ namespace slowmotion return; } - utils::hook::jump(0x140365480, scr_cmd_set_slow_motion); + utils::hook::jump(0x43D2E0_b, scr_cmd_set_slow_motion); } }; } diff --git a/src/client/component/splash.cpp b/src/client/component/splash.cpp index c9ec30be..79e48824 100644 --- a/src/client/component/splash.cpp +++ b/src/client/component/splash.cpp @@ -30,9 +30,9 @@ namespace splash void post_unpack() override { // Disable native splash screen - utils::hook::nop(SELECT_VALUE(0x1403E192E, 0x1405123E2), 5); - utils::hook::jump(SELECT_VALUE(0x1403E2E70, 0x140513AF0), destroy_stub); - utils::hook::jump(SELECT_VALUE(0x1403E2EB0, 0x140513B30), destroy_stub); + utils::hook::set(SELECT_VALUE(0x462B90_b, 0x5BDF20_b), 0xC3); + utils::hook::jump(SELECT_VALUE(0x462E40_b, 0x5BE1D0_b), destroy_stub, true); + utils::hook::jump(SELECT_VALUE(0x462E80_b, 0x5BE210_b), destroy_stub, true); } void pre_destroy() override diff --git a/src/client/component/stats.cpp b/src/client/component/stats.cpp index 99ffdd5f..dae3c9e3 100644 --- a/src/client/component/stats.cpp +++ b/src/client/component/stats.cpp @@ -16,45 +16,35 @@ namespace stats namespace { game::dvar_t* cg_unlock_all_items; + game::dvar_t* cg_unlock_all_loot; utils::hook::detour is_item_unlocked_hook; utils::hook::detour is_item_unlocked_hook2; - utils::hook::detour is_item_unlocked_hook3; - int is_item_unlocked_stub(int a1, void* a2, int a3) + int is_item_unlocked_stub(int a1, void* a2, void* a3, void* a4, int a5, void* a6) { if (cg_unlock_all_items->current.enabled) { return 0; } - return is_item_unlocked_hook.invoke(a1, a2, a3); - } - - int is_item_unlocked_stub2(int a1, void* a2, void* a3, void* a4, int a5, void* a6) - { - if (cg_unlock_all_items->current.enabled) - { - return 0; - } - - return is_item_unlocked_hook2.invoke(a1, a2, a3, a4, a5, a6); - } - - int is_item_unlocked_stub3(int a1) - { - if (cg_unlock_all_items->current.enabled) - { - return 0; - } - - return is_item_unlocked_hook3.invoke(a1); + return is_item_unlocked_hook.invoke(a1, a2, a3, a4, a5, a6); } int is_item_unlocked() { return 0; } + + int is_item_unlocked_stub2(void* a1, void* a2) + { + const auto state = is_item_unlocked_hook2.invoke(a1, a2); + if (state == 15 /*Not In Inventory*/ && cg_unlock_all_loot->current.enabled) + { + return 0; + } + return state; + } } class component final : public component_interface @@ -67,22 +57,25 @@ namespace stats return; } + utils::hook::jump(0x19E6E0_b, is_item_unlocked, true); + if (game::environment::is_dedi()) { - utils::hook::jump(0x140413E60, is_item_unlocked); - utils::hook::jump(0x140413860, is_item_unlocked); - utils::hook::jump(0x140412B70, is_item_unlocked); + utils::hook::jump(0x19E070_b, is_item_unlocked, true); + utils::hook::jump(0x19D390_b, is_item_unlocked, true); + utils::hook::jump(0x19D140_b, is_item_unlocked, true); } else { + is_item_unlocked_hook.create(0x19E070_b, is_item_unlocked_stub); + is_item_unlocked_hook2.create(0x19D140_b, is_item_unlocked_stub2); + cg_unlock_all_items = dvars::register_bool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED, "Whether items should be locked based on the player's stats or always unlocked."); dvars::register_bool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED, "Whether classes should be locked based on the player's stats or always unlocked."); - - is_item_unlocked_hook.create(0x140413E60, is_item_unlocked_stub); - is_item_unlocked_hook2.create(0x140413860, is_item_unlocked_stub2); - is_item_unlocked_hook3.create(0x140412B70, is_item_unlocked_stub3); + cg_unlock_all_loot = dvars::register_bool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED, + "Whether loot should be locked based on the player's stats or always unlocked."); } } }; diff --git a/src/client/component/steam_proxy.cpp b/src/client/component/steam_proxy.cpp index 5fa54708..dcef9d12 100644 --- a/src/client/component/steam_proxy.cpp +++ b/src/client/component/steam_proxy.cpp @@ -42,7 +42,7 @@ namespace steam_proxy #ifndef DEV_BUILD try { - this->start_mod("\xF0\x9F\x90\xA4" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100); + this->start_mod("\xF0\x9F\x8E\xAE" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100); } catch (std::exception& e) { diff --git a/src/client/component/system_check.cpp b/src/client/component/system_check.cpp index 9c88bd5c..794bf375 100644 --- a/src/client/component/system_check.cpp +++ b/src/client/component/system_check.cpp @@ -51,25 +51,26 @@ namespace system_check { static std::unordered_map mp_zone_hashes = { - {"patch_common_mp.ff", "3F44B0CFB0B8E0FBD9687C2942204AB7F11E66E6E15C73B8B4A5EB5920115A31"}, + {"patch_common_mp.ff", "E45EF5F29D12A5A47F405F89FBBEE479C0A90D02141ABF852D481689514134A1"}, }; static std::unordered_map sp_zone_hashes = { // Steam doesn't necessarily deliver this file :( - {"patch_common.ff", "BB0617DD94AF2F511571E7184BBEDE76E64D97E5D0DAFDB457F00717F035EBF0"}, + {"patch_common.ff", "1D32A9770F90ED022AA76F4859B4AB178E194A703383E61AC2CE83B1E828B18F"}, }; return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes)); } + // need to update these values void verify_binary_version() { - const auto value = *reinterpret_cast(0x140001337); - if (value != 0xFFB8006D && value != 0xFFB80080) + const auto value = *reinterpret_cast(0x1337_b); + if (value != 0x60202B6A && value != 0xBC0E9FE) { - throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version(1.4)"); + throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)"); } } } @@ -90,7 +91,7 @@ namespace system_check if (!is_valid()) { MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n" - "Please get the latest officially supported Call of Duty: Modern Warfare Remastered 1.4 files, or you will get random crashes and issues.", + "Please get the latest officially supported Call of Duty: Modern Warfare Remastered files, or you will get random crashes and issues.", "Invalid game files!", MB_ICONINFORMATION); } } diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp index c6b44883..cbaa0f6a 100644 --- a/src/client/component/ui_scripting.cpp +++ b/src/client/component/ui_scripting.cpp @@ -7,164 +7,481 @@ #include "scheduler.hpp" #include "command.hpp" -#include "ui_scripting.hpp" +#include "localized_strings.hpp" +#include "console.hpp" +#include "game_module.hpp" +#include "fps.hpp" +#include "server_list.hpp" +#include "filesystem.hpp" +#include "mods.hpp" +#include "fastfiles.hpp" +#include "scripting.hpp" -#include "game/ui_scripting/lua/engine.hpp" #include "game/ui_scripting/execution.hpp" -#include "game/ui_scripting/lua/error.hpp" +#include "game/scripting/execution.hpp" + +#include "ui_scripting.hpp" #include #include +#include +#include namespace ui_scripting { namespace { - std::unordered_map converted_functions; + std::unordered_map> converted_functions; - utils::hook::detour hksi_lual_error_hook; - utils::hook::detour hksi_lual_error_hook2; utils::hook::detour hks_start_hook; utils::hook::detour hks_shutdown_hook; - utils::hook::detour hks_allocator_hook; - utils::hook::detour hks_frame_hook; - utils::hook::detour lui_error_hook; - utils::hook::detour hksi_hks_error_hook; + utils::hook::detour hks_package_require_hook; - bool error_hook_enabled = false; + utils::hook::detour hks_load_hook; - void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...) + const auto lui_common = utils::nt::load_resource(LUI_COMMON); + const auto lua_json = utils::nt::load_resource(LUA_JSON); + + struct script { - char va_buffer[2048] = {0}; + std::string name; + std::string root; + }; - va_list ap; - va_start(ap, fmt); - vsprintf_s(va_buffer, fmt, ap); - va_end(ap); + struct globals_t + { + std::string in_require_script; + std::vector