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