commit
da90d37846
@ -4,8 +4,7 @@
|
||||
<img alig src="https://cdn.discordapp.com/attachments/895680402142941194/938488726391242842/splash.png"/>
|
||||
</p>
|
||||
|
||||
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)
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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/**.*"}
|
||||
|
||||
|
145
src/client/component/arxan.cpp
Normal file
145
src/client/component/arxan.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
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<decltype(NtQueryInformationProcess)*>(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<PPROCESS_BASIC_INFORMATION>(info)->Reserved3 = PVOID(DWORD64(explorer_pid));
|
||||
}
|
||||
else if (info_class == 30) // ProcessDebugObjectHandle
|
||||
{
|
||||
*static_cast<HANDLE*>(info) = nullptr;
|
||||
|
||||
return 0xC0000353;
|
||||
}
|
||||
else if (info_class == 7) // ProcessDebugPort
|
||||
{
|
||||
*static_cast<HANDLE*>(info) = nullptr;
|
||||
}
|
||||
else if (info_class == 31)
|
||||
{
|
||||
*static_cast<ULONG*>(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<decltype(NtClose)*>(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<PDWORD>(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<void*>("NtClose"), nt_close_stub);
|
||||
nt_query_information_process_hook.create(ntdll.get_proc<void*>("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)
|
@ -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", []()
|
||||
|
@ -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<size_t>(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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
REGISTER_COMPONENT(bots::component)
|
||||
|
@ -27,14 +27,28 @@ namespace branding
|
||||
const auto* const build_num = ui_get_formatted_build_number_hook.invoke<const char*>();
|
||||
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<float>(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<float>(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)
|
46
src/client/component/bullet.cpp
Normal file
46
src/client/component/bullet.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
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<float>(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<float>::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)
|
@ -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<void*>(0x2E6F588_b));
|
||||
utils::hook::inject(0x18A980_b, reinterpret_cast<void*>(0x2E6F588_b));
|
||||
utils::hook::call(0x33EDEC_b, ui_get_font_handle_stub);
|
||||
|
||||
// set text style to 0 (non-blurry)
|
||||
utils::hook::set<uint8_t>(0x14024F5DD, 0);
|
||||
utils::hook::set<uint8_t>(0x1400AAA1C, 0);
|
||||
utils::hook::set<uint8_t>(0x14024802E, 0);
|
||||
utils::hook::set<uint8_t>(0x18A9F2_b, 0);
|
||||
utils::hook::set<uint8_t>(0x0F7151_b, 0);
|
||||
utils::hook::set<uint8_t>(0x33EE0E_b, 0);
|
||||
|
||||
localized_strings::override("EXE_SAY", "^3Match^7");
|
||||
localized_strings::override("EXE_SAYTEAM", "^5Team^7");
|
||||
|
@ -96,7 +96,7 @@ namespace colors
|
||||
const size_t unk, const size_t unk2)
|
||||
{
|
||||
// CL_GetClientName (CL_GetClientNameAndClantag?)
|
||||
const auto result = utils::hook::invoke<size_t>(0x14025BAA0, local_client_num, index, buf, size, unk, unk2);
|
||||
const auto result = utils::hook::invoke<size_t>(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<DWORD*>(SELECT_VALUE(0x14F142FF8, 0x14FE70634)); // H1(1.4)
|
||||
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x0, 0xEA749B4_b)); // 1.15
|
||||
}
|
||||
else if (index == '9')
|
||||
{
|
||||
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x14F142FFC, 0x14FE70638)); // H1(1.4)
|
||||
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x0, 0xEA749B8_b)); // 1.15
|
||||
}
|
||||
else if (index == ':')
|
||||
{
|
||||
@ -121,7 +121,7 @@ namespace colors
|
||||
}
|
||||
else if (index == ';')
|
||||
{
|
||||
*color = *reinterpret_cast<DWORD*>(SELECT_VALUE(0x14F143004, 0x14FE70640)); // H1(1.4)
|
||||
*color = *reinterpret_cast<DWORD*>(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
|
||||
|
@ -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<std::string, std::function<void(params&)>> handlers;
|
||||
std::unordered_map<std::string, std::function<void(int, params_sv&)>> 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<int*>(0x142623FB4);
|
||||
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0);
|
||||
auto& com_num_console_lines = *reinterpret_cast<int*>(0x35634B8_b);
|
||||
auto* com_console_lines = reinterpret_cast<char**>(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<void>(0x1400D8210);
|
||||
parse_commandline_hook.invoke<void>();
|
||||
}
|
||||
|
||||
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<int*>(0x142623FB4);
|
||||
auto* com_console_lines = reinterpret_cast<char**>(0x142623FC0);
|
||||
auto& com_num_console_lines = *reinterpret_cast<int*>(0x35634B8_b);
|
||||
auto* com_console_lines = reinterpret_cast<char**>(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<void(const params&)>& 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)
|
||||
|
@ -1,14 +1,17 @@
|
||||
#include <std_include.hpp>
|
||||
#include "console.hpp"
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "rcon.hpp"
|
||||
|
||||
#include <utils/thread.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
#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<std::string>;
|
||||
utils::concurrency::container<message_queue> 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<std::string> history{};
|
||||
} con{};
|
||||
|
||||
void set_cursor_pos(int x)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO info{};
|
||||
GetConsoleScreenBufferInfo(OUTPUT_HANDLE, &info);
|
||||
info.dwCursorPosition.X = static_cast<short>(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 <typename... Args>
|
||||
int invoke_printf(const char* fmt, Args&&... args)
|
||||
{
|
||||
return printf_hook.invoke<int>(fmt, std::forward<Args>(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<size_t>(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<size_t>(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<int>(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<int>(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<int>(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<std::string> 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<HWND*>((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<HWND*>(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)
|
||||
|
@ -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>(args)...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void debug(const char* fmt, Args&&... args)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
print(con_type_debug, fmt, std::forward<Args>(args)...);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void warn(const char* fmt, Args&&... args)
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ namespace dedicated
|
||||
initialized = true;
|
||||
|
||||
// R_LoadGraphicsAssets
|
||||
utils::hook::invoke<void>(0x1405DF4B0);
|
||||
utils::hook::invoke<void>(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<void>(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<uint8_t>(0x1400DC1D0, 0xC3); // don't save config file
|
||||
utils::hook::set<uint8_t>(0x140274710, 0xC3); // disable self-registration
|
||||
utils::hook::set<uint8_t>(0x140515890, 0xC3); // init sound system (1)
|
||||
utils::hook::set<uint8_t>(0x1406574F0, 0xC3); // init sound system (2)
|
||||
utils::hook::set<uint8_t>(0x140620D10, 0xC3); // render thread
|
||||
utils::hook::set<uint8_t>(0x14025B850, 0xC3); // called from Com_Frame, seems to do renderer stuff
|
||||
utils::hook::set<uint8_t>(0x1402507B0, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
|
||||
utils::hook::set<uint8_t>(0x1405D5178, 0x00); // r_loadForRenderer default to 0
|
||||
utils::hook::set<uint8_t>(0x14050C2D0, 0xC3); // recommended settings check - TODO: Check hook
|
||||
utils::hook::set<uint8_t>(0x140514C00, 0xC3); // some mixer-related function called on shutdown
|
||||
utils::hook::set<uint8_t>(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<uint8_t>(0x17F470_b, 0xC3); // don't save config file
|
||||
utils::hook::set<uint8_t>(0x351AA0_b, 0xC3); // disable self-registration
|
||||
utils::hook::set<uint8_t>(0x5BF4E0_b, 0xC3); // init sound system (1)
|
||||
utils::hook::set<uint8_t>(0x701820_b, 0xC3); // init sound system (2)
|
||||
utils::hook::set<uint8_t>(0x701850_b, 0xC3); // init sound system (3)
|
||||
utils::hook::set<uint8_t>(0x6C9B10_b, 0xC3); // render thread
|
||||
utils::hook::set<uint8_t>(0x343950_b, 0xC3); // called from Com_Frame, seems to do renderer stuff
|
||||
utils::hook::set<uint8_t>(0x12CCA0_b, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
|
||||
utils::hook::set<uint8_t>(0x67ADCE_b, 0x00); // r_loadForRenderer default to 0
|
||||
utils::hook::set<uint8_t>(0x5B7AF0_b, 0xC3); // recommended settings check
|
||||
utils::hook::set<uint8_t>(0x5BE850_b, 0xC3); // some mixer-related function called on shutdown
|
||||
utils::hook::set<uint8_t>(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<uint8_t>(0x140091840, 0xC3); // something to do with blendShapeVertsView
|
||||
utils::hook::nop(0x140659A0D, 8); // sound thing
|
||||
utils::hook::set<uint8_t>(0xAA290_b, 0xC3); // something to do with blendShapeVertsView
|
||||
utils::hook::nop(0x70465D_b, 8); // sound thing
|
||||
|
||||
// (COULD NOT FIND IN H1)
|
||||
// utils::hook::set<uint8_t>(0x1404D6960, 0xC3); // cpu detection stuff?
|
||||
utils::hook::set<uint8_t>(0x1405E97F0, 0xC3); // gfx stuff during fastfile loading
|
||||
utils::hook::set<uint8_t>(0x1405E9700, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405E9790, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1402C1180, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405E9750, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405AD5B0, 0xC3); // directx stuff
|
||||
utils::hook::set<uint8_t>(0x1405DB150, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x140625220, 0xC3); // ^ - mutex
|
||||
utils::hook::set<uint8_t>(0x1405DB650, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x690F30_b, 0xC3); // gfx stuff during fastfile loading
|
||||
utils::hook::set<uint8_t>(0x690E00_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x690ED0_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x39B980_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x690E50_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x651BA0_b, 0xC3); // directx stuff
|
||||
utils::hook::set<uint8_t>(0x681950_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x6CE390_b, 0xC3); // ^ - mutex
|
||||
utils::hook::set<uint8_t>(0x681ED0_b, 0xC3); // ^
|
||||
|
||||
utils::hook::set<uint8_t>(0x14008B5F0, 0xC3); // rendering stuff
|
||||
utils::hook::set<uint8_t>(0x1405DB8B0, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405DB9C0, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405DC050, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405DCBA0, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405DD240, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x0A3CD0_b, 0xC3); // rendering stuff
|
||||
utils::hook::set<uint8_t>(0x682150_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x682260_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x6829C0_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x6834A0_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x683B40_b, 0xC3); // ^
|
||||
|
||||
// shaders
|
||||
utils::hook::set<uint8_t>(0x1400916A0, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x140091610, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x14061ACC0, 0xC3); // ^ - mutex
|
||||
utils::hook::set<uint8_t>(0x0AA090_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x0A9FE0_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x6C38D0_b, 0xC3); // ^ - mutex
|
||||
|
||||
utils::hook::set<uint8_t>(0x140516080, 0xC3); // idk
|
||||
utils::hook::set<uint8_t>(0x1405AE5F0, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x5BFD10_b, 0xC3); // idk
|
||||
utils::hook::set<uint8_t>(0x652E10_b, 0xC3); // ^
|
||||
|
||||
utils::hook::set<uint8_t>(0x1405E0B30, 0xC3); // R_Shutdown
|
||||
utils::hook::set<uint8_t>(0x1405AE400, 0xC3); // shutdown stuff
|
||||
utils::hook::set<uint8_t>(0x1405E0C00, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x1405DFE50, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x687D20_b, 0xC3); // R_Shutdown
|
||||
utils::hook::set<uint8_t>(0x652BA0_b, 0xC3); // shutdown stuff
|
||||
utils::hook::set<uint8_t>(0x687DF0_b, 0xC3); // ^
|
||||
utils::hook::set<uint8_t>(0x686DE0_b, 0xC3); // ^
|
||||
|
||||
// utils::hook::set<uint8_t>(0x1404B67E0, 0xC3); // sound crashes (H1 - questionable, function looks way different)
|
||||
|
||||
utils::hook::set<uint8_t>(0x14048B660, 0xC3); // disable host migration
|
||||
utils::hook::set<uint8_t>(0x556250_b, 0xC3); // disable host migration
|
||||
|
||||
utils::hook::set<uint8_t>(0x14042B2E0, 0xC3); // render synchronization lock
|
||||
utils::hook::set<uint8_t>(0x14042B210, 0xC3); // render synchronization unlock
|
||||
utils::hook::set<uint8_t>(0x4F7C10_b, 0xC3); // render synchronization lock
|
||||
utils::hook::set<uint8_t>(0x4F7B40_b, 0xC3); // render synchronization unlock
|
||||
|
||||
utils::hook::set<uint8_t>(0x140176D2D, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua
|
||||
utils::hook::set<uint8_t>(0x27AA9D_b, 0xEB); // LUI: Unable to start the LUI system due to errors in main.lua
|
||||
utils::hook::set<uint8_t>(0x27AAC5_b, 0xEB); // LUI: Unable to start the LUI system due to errors in depot.lua
|
||||
utils::hook::set<uint8_t>(0x27AADC_b, 0xEB); // ^
|
||||
|
||||
utils::hook::nop(0x140506ECE, 5); // Disable sound pak file loading
|
||||
utils::hook::nop(0x140506ED6, 2); // ^
|
||||
utils::hook::set<uint8_t>(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<uint8_t>(0x3A0BA0_b, 0xC3); // Disable image pak file loading
|
||||
|
||||
// Reduce min required memory
|
||||
utils::hook::set<uint64_t>(0x14050C717, 0x80000000);
|
||||
utils::hook::set<uint64_t>(0x5B7F37_b, 0x80000000);
|
||||
|
||||
utils::hook::set(0x1402BF7F0, 0xC3); // some loop
|
||||
utils::hook::set(0x14007E150, 0xC3); // related to shader caching / techsets / fastfiles
|
||||
utils::hook::set<uint8_t>(0x399E10_b, 0xC3); // some loop
|
||||
utils::hook::set<uint8_t>(0x1D48B0_b, 0xC3); // related to shader caching / techsets / fastfilesc
|
||||
utils::hook::set<uint8_t>(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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <std_include.hpp>
|
||||
#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<int>(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);
|
||||
}
|
||||
};
|
||||
|
@ -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<bool>(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<uint8_t>(0x1405FCA00, 0xC3); // bdAuthSteam
|
||||
utils::hook::set<uint8_t>(0x140333A00, 0xC3); // dwNet
|
||||
utils::hook::set<uint8_t>(0x68DDA0_b, 0xC3); // bdAuthSteam
|
||||
utils::hook::set<uint8_t>(0x366600_b, 0xC3); // dwNet
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::set<uint8_t>(0x140715039, 0x0); // CURLOPT_SSL_VERIFYPEER
|
||||
utils::hook::set<uint8_t>(0x140715025, 0xAF); // CURLOPT_SSL_VERIFYHOST
|
||||
utils::hook::set<uint8_t>(0x14095433C, 0x0); // HTTPS -> HTTP
|
||||
utils::hook::set<uint8_t>(0x7C0AD9_b, 0x0); // CURLOPT_SSL_VERIFYPEER
|
||||
utils::hook::set<uint8_t>(0x7C0AC5_b, 0xAF); // CURLOPT_SSL_VERIFYHOST
|
||||
utils::hook::set<uint8_t>(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<void*>(0x8D0298_b),
|
||||
"http://prod.umbrella.demonware.net/v1.0/", sizeof("http://prod.umbrella.demonware.net/v1.0/"));
|
||||
std::memcpy(reinterpret_cast<void*>(0x8D05A8_b),
|
||||
"http://prod.uno.demonware.net/v1.0/", sizeof("http://prod.uno.demonware.net/v1.0/"));
|
||||
std::memcpy(reinterpret_cast<void*>(0x9EDB08_b), "http://%s:%d/auth/", sizeof("http://%s:%d/auth/"));
|
||||
|
||||
utils::hook::set<uint8_t>(0x14047F290, 0xC3); // SV_SendMatchData
|
||||
utils::hook::set<uint8_t>(0x140598990, 0xC3); // Live_CheckForFullDisconnect
|
||||
// utils::hook::set<uint8_t>(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<uint8_t>(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
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "materials.hpp"
|
||||
#include "discord.hpp"
|
||||
|
||||
#include "ui_scripting.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
@ -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<int*>(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)
|
||||
REGISTER_COMPONENT(discord::component)
|
||||
|
@ -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<uint8_t>(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);
|
||||
}
|
||||
};
|
||||
|
@ -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<std::string, dvar_string> register_string_overrides;
|
||||
static std::unordered_map<std::string, dvar_vector2> register_vector2_overrides;
|
||||
static std::unordered_map<std::string, dvar_vector3> register_vector3_overrides;
|
||||
static std::unordered_map<std::string, dvar_enum> register_enum_overrides;
|
||||
|
||||
static std::unordered_map<std::string, bool> set_bool_overrides;
|
||||
static std::unordered_map<std::string, float> 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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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<game::dvar_t*>(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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<uint64_t>(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)
|
||||
|
@ -1,12 +1,16 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "fastfiles.hpp"
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
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<void>(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<char*>(&header.scriptfile->compressedLen), 4);
|
||||
buffer.append(reinterpret_cast<char*>(&header.scriptfile->len), 4);
|
||||
buffer.append(reinterpret_cast<char*>(&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<game::XAssetHeader>(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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -114,7 +114,7 @@ namespace fonts
|
||||
}
|
||||
|
||||
data_.fonts.clear();
|
||||
utils::hook::set<int>(SELECT_VALUE(0x14F09DBB8, 0x14FD61EE8), 0); // reset registered font count
|
||||
utils::hook::set<int>(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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
#include "dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -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<void>(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<void>();
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
|
@ -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<int>(i), game::Dvar_ValueToString(dvar, dvar->current),
|
||||
draw_hint_text(static_cast<int>(i), game::Dvar_ValueToString(dvar, true, dvar->current),
|
||||
dvars::con_inputDvarValueColor->current.vector, offset);
|
||||
|
||||
draw_hint_text(static_cast<int>(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;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "game_module.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <game/game.hpp>
|
||||
|
||||
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<DWORD>(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<DWORD>(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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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<void>(0x1401D5360, ps);
|
||||
jump_apply_slowdown_hook.invoke<void>(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<int>(0x140326CE0, entity); // StuckInClient
|
||||
stuck_in_client_hook.invoke<void>(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<void>(0x1403FF860,
|
||||
results, start, end, bounds, capsule, contents, origin, angles); // CM_TransformedCapsuleTrace
|
||||
cm_transformed_capsule_trace_hook.invoke<void>(results, start, end,
|
||||
bounds, capsule, contents, origin, angles);
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +58,7 @@ namespace gameplay
|
||||
{
|
||||
if (jump_enableFallDamage->current.enabled)
|
||||
{
|
||||
utils::hook::invoke<void>(0x1401E2D00, ps, pml);
|
||||
pm_crashland_hook.invoke<void>(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<int64_t>(&dvars::pm_bouncing)));
|
||||
a.mov(al, byte_ptr(rax, 0x10));
|
||||
a.cmp(byte_ptr(rbp, -0x2D), al);
|
||||
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&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<int64_t>(&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<int64_t>(&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<int64_t>(&dvars::g_gravity)));
|
||||
a.mov(eax, dword_ptr(rax, 0x10));
|
||||
a.mov(word_ptr(rbx, 0x34), ax);
|
||||
a.mov(rax, qword_ptr(reinterpret_cast<int64_t>(&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<void>(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<void>(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<void>(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<int>::min(), std::numeric_limits<int>::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<short>::min(),
|
||||
std::numeric_limits<short>::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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include "game_console.hpp"
|
||||
#include "ui_scripting.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
@ -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 <utils/hook.hpp>
|
||||
|
||||
#include "logfile.hpp"
|
||||
|
||||
namespace logfile
|
||||
{
|
||||
std::unordered_map<const char*, sol::protected_function> 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<sol::protected_function> player_killed_callbacks;
|
||||
std::vector<sol::protected_function> player_damage_callbacks;
|
||||
|
||||
@ -58,7 +56,7 @@ namespace logfile
|
||||
|
||||
std::string convert_mod(const int meansOfDeath)
|
||||
{
|
||||
const auto value = reinterpret_cast<game::scr_string_t**>(0x140FEC3F0)[meansOfDeath];
|
||||
const auto value = reinterpret_cast<game::scr_string_t**>(0x10B5290_b)[meansOfDeath];
|
||||
const auto string = game::SL_ConvertToString(*value);
|
||||
return string;
|
||||
}
|
||||
@ -68,7 +66,7 @@ namespace logfile
|
||||
const bool isAlternate, const float* vDir, const unsigned int hitLoc, int psTimeOffset, int deathAnimDuration)
|
||||
{
|
||||
{
|
||||
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
||||
const std::string hitloc = reinterpret_cast<const char**>(0x10B5370_b)[hitLoc];
|
||||
const auto mod_ = convert_mod(meansOfDeath);
|
||||
|
||||
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
||||
@ -110,7 +108,7 @@ namespace logfile
|
||||
const float* vDir, const unsigned int hitLoc, const int timeOffset)
|
||||
{
|
||||
{
|
||||
const std::string hitloc = reinterpret_cast<const char**>(0x140FEC4D0)[hitLoc];
|
||||
const std::string hitloc = reinterpret_cast<const char**>(0x10B5370_b)[hitLoc];
|
||||
const auto mod_ = convert_mod(meansOfDeath);
|
||||
|
||||
const auto weapon_ = get_weapon_name(weapon, isAlternate);
|
||||
@ -147,51 +145,6 @@ namespace logfile
|
||||
meansOfDeath, weapon, isAlternate, vPoint, vDir, hitLoc, timeOffset);
|
||||
}
|
||||
|
||||
void client_command_stub(const int clientNum)
|
||||
{
|
||||
auto self = &game::mp::g_entities[clientNum];
|
||||
char cmd[1024] = {0};
|
||||
|
||||
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
||||
|
||||
if (cmd == "say"s || cmd == "say_team"s)
|
||||
{
|
||||
auto hidden = false;
|
||||
std::string message(game::ConcatArgs(1));
|
||||
|
||||
hidden = message[1] == '/';
|
||||
message.erase(0, hidden ? 2 : 1);
|
||||
|
||||
scheduler::once([cmd, message, self]()
|
||||
{
|
||||
const scripting::entity level{*game::levelEntityId};
|
||||
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
|
||||
|
||||
scripting::notify(level, cmd, {player, message});
|
||||
scripting::notify(player, cmd, {message});
|
||||
}, scheduler::pipeline::server);
|
||||
|
||||
if (hidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// ClientCommand
|
||||
return utils::hook::invoke<void>(0x140336000, clientNum);
|
||||
}
|
||||
|
||||
void g_shutdown_game_stub(const int freeScripts)
|
||||
{
|
||||
{
|
||||
const scripting::entity level{*game::levelEntityId};
|
||||
scripting::notify(level, "shutdownGame_called", {1});
|
||||
}
|
||||
|
||||
// G_ShutdownGame
|
||||
return utils::hook::invoke<void>(0x140345A60, freeScripts);
|
||||
}
|
||||
|
||||
unsigned int local_id_to_entity(unsigned int local_id)
|
||||
{
|
||||
const auto variable = game::scr_VarGlob->objectVariableValue[local_id];
|
||||
@ -254,7 +207,7 @@ namespace logfile
|
||||
a.inc(r14);
|
||||
a.mov(dword_ptr(rbp, 0xA4), r15d);
|
||||
|
||||
a.jmp(SELECT_VALUE(0x140376663, 0x140444653));
|
||||
a.jmp(SELECT_VALUE(0x3CA153_b, 0x5111B3_b));
|
||||
|
||||
a.bind(replace);
|
||||
|
||||
@ -291,25 +244,53 @@ namespace logfile
|
||||
hook_enabled = false;
|
||||
}
|
||||
|
||||
bool client_command_stub(const int client_num)
|
||||
{
|
||||
auto self = &game::mp::g_entities[client_num];
|
||||
char cmd[1024] = {0};
|
||||
|
||||
game::SV_Cmd_ArgvBuffer(0, cmd, 1024);
|
||||
|
||||
if (cmd == "say"s || cmd == "say_team"s)
|
||||
{
|
||||
auto hidden = false;
|
||||
std::string message(game::ConcatArgs(1));
|
||||
|
||||
hidden = message[1] == '/';
|
||||
message.erase(0, hidden ? 2 : 1);
|
||||
|
||||
scheduler::once([cmd, message, self, hidden]()
|
||||
{
|
||||
const scripting::entity level{*game::levelEntityId};
|
||||
const scripting::entity player{game::Scr_GetEntityId(self->s.entityNum, 0)};
|
||||
|
||||
scripting::notify(level, cmd, {player, message, hidden});
|
||||
scripting::notify(player, cmd, {message, hidden});
|
||||
}, scheduler::pipeline::server);
|
||||
|
||||
if (hidden)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
utils::hook::jump(SELECT_VALUE(0x140376655, 0x140444645), utils::hook::assemble(vm_execute_stub), true);
|
||||
utils::hook::jump(SELECT_VALUE(0x3CA145_b, 0x5111A5_b), utils::hook::assemble(vm_execute_stub), true);
|
||||
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::call(0x14048191D, client_command_stub);
|
||||
|
||||
scr_player_damage_hook.create(0x14037DC50, scr_player_damage_stub);
|
||||
scr_player_killed_hook.create(0x14037DF30, scr_player_killed_stub);
|
||||
|
||||
utils::hook::call(0x140484EC0, g_shutdown_game_stub);
|
||||
utils::hook::call(0x1404853C1, g_shutdown_game_stub);
|
||||
scr_player_damage_hook.create(0x1CE780_b, scr_player_damage_stub);
|
||||
scr_player_killed_hook.create(0x1CEA60_b, scr_player_killed_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1,5 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/scripting/entity.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
#include "game/scripting/lua/value_conversion.hpp"
|
||||
#include "game/scripting/lua/error.hpp"
|
||||
|
||||
namespace logfile
|
||||
{
|
||||
extern std::unordered_map<const char*, sol::protected_function> vm_execute_hooks;
|
||||
@ -10,4 +15,6 @@ namespace logfile
|
||||
|
||||
void enable_vm_execute_hook();
|
||||
void disable_vm_execute_hook();
|
||||
|
||||
bool client_command_stub(const int client_num);
|
||||
}
|
@ -110,50 +110,6 @@ namespace logger
|
||||
|
||||
console::info(buffer);
|
||||
}
|
||||
|
||||
void lui_error()
|
||||
{
|
||||
utils::hook::call(0x140162809, print_warning);
|
||||
utils::hook::call(0x140162815, print_warning);
|
||||
utils::hook::call(0x14016281D, print_warning);
|
||||
utils::hook::call(0x140162829, print_warning);
|
||||
|
||||
utils::hook::call(0x140162E32, print_warning);
|
||||
utils::hook::call(0x140162E3E, print_warning);
|
||||
utils::hook::call(0x140162E46, print_warning);
|
||||
utils::hook::call(0x140162E52, print_warning);
|
||||
|
||||
utils::hook::call(0x140168435, print_warning);
|
||||
utils::hook::call(0x140168441, print_warning);
|
||||
utils::hook::call(0x140168449, print_warning);
|
||||
utils::hook::call(0x140168455, print_warning);
|
||||
|
||||
utils::hook::call(0x14016914D, print_warning);
|
||||
utils::hook::call(0x140169161, print_warning);
|
||||
|
||||
utils::hook::call(0x140169C04, print_warning);
|
||||
utils::hook::call(0x140169C0C, print_warning);
|
||||
utils::hook::call(0x140169C18, print_warning);
|
||||
|
||||
utils::hook::call(0x140169CB7, print_warning);
|
||||
utils::hook::call(0x140169CDE, print_warning);
|
||||
utils::hook::call(0x140169CEA, print_warning);
|
||||
utils::hook::call(0x140169D03, print_warning);
|
||||
|
||||
utils::hook::call(0x14016BE72, print_warning);
|
||||
utils::hook::call(0x14016C020, print_warning);
|
||||
}
|
||||
|
||||
void lui_interface_debug_print()
|
||||
{
|
||||
utils::hook::call(0x14015C0B2, print_warning);
|
||||
utils::hook::call(0x140162453, print_warning);
|
||||
utils::hook::call(0x1401625DF, print_warning);
|
||||
utils::hook::call(0x14016713C, print_dev);
|
||||
utils::hook::call(0x1401687CD, print_dev);
|
||||
utils::hook::call(0x14016BB8A, print_dev);
|
||||
utils::hook::call(0x14016CA9C, print_dev);
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -161,15 +117,12 @@ namespace logger
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_mp())
|
||||
if (!game::environment::is_dedi())
|
||||
{
|
||||
lui_error();
|
||||
lui_interface_debug_print();
|
||||
}
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
utils::hook::call(0x14051347F, print_com_error);
|
||||
// lua stuff
|
||||
utils::hook::jump(SELECT_VALUE(0x106010_b, 0x27CBB0_b), print_dev); // debug
|
||||
utils::hook::jump(SELECT_VALUE(0x107680_b, 0x27E210_b), print_error); // error
|
||||
utils::hook::jump(SELECT_VALUE(0x0E6E30_b, 0x1F6140_b), printf); // print
|
||||
}
|
||||
|
||||
com_error_hook.create(game::Com_Error, com_error_stub);
|
||||
|
@ -15,14 +15,6 @@ namespace lui
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// Don't show create cod account popup
|
||||
//utils::hook::set<uint32_t>(0x14017C957, 0);
|
||||
|
||||
//#ifdef _DEBUG
|
||||
// Enable development menus (causes issues in sp)
|
||||
//utils::hook::set<uint32_t>(SELECT_VALUE(0x1400B4ABC, 0x1401AB779), 1);
|
||||
//#endif
|
||||
|
||||
command::add("lui_open", [](const command::params& params)
|
||||
{
|
||||
if (params.size() <= 1)
|
||||
|
@ -9,9 +9,10 @@
|
||||
|
||||
namespace map_rotation
|
||||
{
|
||||
DWORD previousPriority;
|
||||
namespace
|
||||
{
|
||||
DWORD previous_priority{};
|
||||
|
||||
void set_dvar(const std::string& dvar, const std::string& value)
|
||||
{
|
||||
command::execute(utils::string::va("%s \"%s\"", dvar.data(), value.data()), true);
|
||||
@ -84,10 +85,10 @@ namespace map_rotation
|
||||
scheduler::on_game_initialized([]()
|
||||
{
|
||||
//printf("=======================setting OLD priority=======================\n");
|
||||
SetPriorityClass(GetCurrentProcess(), previousPriority);
|
||||
SetPriorityClass(GetCurrentProcess(), previous_priority);
|
||||
}, scheduler::pipeline::main, 1s);
|
||||
|
||||
previousPriority = GetPriorityClass(GetCurrentProcess());
|
||||
previous_priority = GetPriorityClass(GetCurrentProcess());
|
||||
//printf("=======================setting NEW priority=======================\n");
|
||||
SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
|
||||
}
|
||||
@ -147,7 +148,6 @@ namespace map_rotation
|
||||
return scheduler::cond_end;
|
||||
}, scheduler::pipeline::main, 1s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -170,9 +170,9 @@ namespace map_rotation
|
||||
command::add("map_rotate", &perform_map_rotation);
|
||||
|
||||
// Hook GScr_ExitLevel
|
||||
utils::hook::jump(0x140376630, &trigger_map_rotation); // not sure if working
|
||||
utils::hook::jump(0xE2670_b, &trigger_map_rotation, true); // not sure if working
|
||||
|
||||
previousPriority = GetPriorityClass(GetCurrentProcess());
|
||||
previous_priority = GetPriorityClass(GetCurrentProcess());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -203,8 +203,8 @@ namespace materials
|
||||
}
|
||||
|
||||
material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub);
|
||||
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1401D3180, 0x1402C6260), db_material_streaming_fail_stub);
|
||||
db_get_material_index_hook.create(SELECT_VALUE(0x1401CAD00, 0x1402BBB20), db_get_material_index_stub);
|
||||
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1FB400_b, 0x3A1600_b), db_material_streaming_fail_stub);
|
||||
db_get_material_index_hook.create(SELECT_VALUE(0x1F1D80_b, 0x396000_b), db_get_material_index_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace mods
|
||||
utils::io::create_directory("mods");
|
||||
}
|
||||
|
||||
db_release_xassets_hook.create(SELECT_VALUE(0x1401CD560, 0x1402BF160), db_release_xassets_stub);
|
||||
db_release_xassets_hook.create(SELECT_VALUE(0x1F4DB0_b, 0x399740_b), db_release_xassets_stub);
|
||||
|
||||
command::add("loadmod", [](const command::params& params)
|
||||
{
|
||||
|
@ -60,25 +60,25 @@ namespace network
|
||||
// Command handled
|
||||
a.popad64();
|
||||
a.mov(al, 1);
|
||||
a.jmp(0x140252AF8);
|
||||
a.jmp(0x12FCAA_b);
|
||||
|
||||
a.bind(return_unhandled);
|
||||
a.popad64();
|
||||
a.jmp(0x14025234C);
|
||||
a.jmp(0x12F3AC_b);
|
||||
}
|
||||
|
||||
int net_compare_base_address(const game::netadr_s* a1, const game::netadr_s* a2)
|
||||
int net_compare_base_address(const game::netadr_s* a, const game::netadr_s* b)
|
||||
{
|
||||
if (a1->type == a2->type)
|
||||
if (a->type == b->type)
|
||||
{
|
||||
switch (a1->type)
|
||||
switch (a->type)
|
||||
{
|
||||
case game::netadrtype_t::NA_BOT:
|
||||
case game::netadrtype_t::NA_LOOPBACK:
|
||||
return a1->port == a2->port;
|
||||
return a->port == b->port;
|
||||
|
||||
case game::netadrtype_t::NA_IP:
|
||||
return !memcmp(a1->ip, a2->ip, 4);
|
||||
return !memcmp(a->ip, b->ip, 4);
|
||||
case game::netadrtype_t::NA_BROADCAST:
|
||||
return true;
|
||||
default:
|
||||
@ -89,9 +89,9 @@ namespace network
|
||||
return false;
|
||||
}
|
||||
|
||||
int net_compare_address(const game::netadr_s* a1, const game::netadr_s* a2)
|
||||
int net_compare_address(const game::netadr_s* a, const game::netadr_s* b)
|
||||
{
|
||||
return net_compare_base_address(a1, a2) && a1->port == a2->port;
|
||||
return net_compare_base_address(a, b) && a->port == b->port;
|
||||
}
|
||||
|
||||
void reconnect_migratated_client(void*, game::netadr_s* from, const int, const int, const char*,
|
||||
@ -110,17 +110,17 @@ namespace network
|
||||
if (net_interface && net_interface != "localhost"s)
|
||||
{
|
||||
// Sys_StringToSockaddr
|
||||
utils::hook::invoke<void>(0x1404F6580, net_interface, &address);
|
||||
utils::hook::invoke<void>(0x59E810_b, net_interface, &address);
|
||||
}
|
||||
|
||||
address.sin_family = AF_INET;
|
||||
address.sin_port = ntohs(static_cast<short>(port));
|
||||
|
||||
const auto sock = ::socket(AF_INET, SOCK_DGRAM, protocol);
|
||||
const auto sock = socket(AF_INET, SOCK_DGRAM, protocol);
|
||||
|
||||
u_long arg = 1;
|
||||
ioctlsocket(sock, FIONBIO, &arg);
|
||||
char optval[4] = { 1 };
|
||||
char optval[4] = {1};
|
||||
setsockopt(sock, 0xFFFF, 32, optval, 4);
|
||||
|
||||
if (bind(sock, reinterpret_cast<sockaddr*>(&address), sizeof(address)) != -1)
|
||||
@ -138,10 +138,10 @@ namespace network
|
||||
get_callbacks()[utils::string::to_lower(command)] = callback;
|
||||
}
|
||||
|
||||
int dw_send_to_stub(const int size, const char* src, game::netadr_s* a3)
|
||||
int dw_send_to_stub(const int size, const char* src, game::netadr_s* to)
|
||||
{
|
||||
sockaddr s = {};
|
||||
game::NetadrToSockadr(a3, &s);
|
||||
game::NetadrToSockadr(to, &s);
|
||||
return sendto(*game::query_socket, src, size, 0, &s, 16) >= 0;
|
||||
}
|
||||
|
||||
@ -229,53 +229,47 @@ namespace network
|
||||
}
|
||||
|
||||
// redirect dw_sendto to raw socket
|
||||
//utils::hook::jump(0x1404D850A, reinterpret_cast<void*>(0x1404D849A));
|
||||
utils::hook::call(0x140513467, dw_send_to_stub);
|
||||
utils::hook::jump(0x5EEC90_b, dw_send_to_stub);
|
||||
utils::hook::jump(game::Sys_SendPacket, dw_send_to_stub);
|
||||
|
||||
// intercept command handling
|
||||
utils::hook::jump(0x140252327, utils::hook::assemble(handle_command_stub), true);
|
||||
utils::hook::jump(0x12F387_b, utils::hook::assemble(handle_command_stub), true);
|
||||
|
||||
// handle xuid without secure connection
|
||||
utils::hook::nop(0x140486AAF, 6);
|
||||
utils::hook::nop(0x554222_b, 6);
|
||||
|
||||
utils::hook::jump(0x140424F20, net_compare_address);
|
||||
utils::hook::jump(0x140424F70, net_compare_base_address);
|
||||
utils::hook::jump(0x4F1800_b, net_compare_address);
|
||||
utils::hook::jump(0x4F1850_b, net_compare_base_address);
|
||||
|
||||
// don't establish secure conenction
|
||||
utils::hook::set<uint8_t>(0x14027EA4D, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x14027EB1E, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x14027EF8D, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x14025081F, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x358C8D_b, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x358D5E_b, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x3591CD_b, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x12CD0F_b, 0xEB);
|
||||
|
||||
// ignore unregistered connection
|
||||
utils::hook::jump(0x140480F46, 0x140480EE5);
|
||||
utils::hook::set<uint8_t>(0x140480F3B, 0xEB);
|
||||
utils::hook::jump(0x54E2D1_b, 0x54E270_b, true);
|
||||
utils::hook::set<uint8_t>(0x54E2C6_b, 0xEB);
|
||||
|
||||
// disable xuid verification
|
||||
utils::hook::set<uint8_t>(0x14005B62D, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x14005B649, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x728BF_b, 0xEB);
|
||||
|
||||
// disable xuid verification
|
||||
utils::hook::nop(0x14048382C, 2);
|
||||
utils::hook::set<uint8_t>(0x140483889, 0xEB);
|
||||
utils::hook::nop(0x5509D9_b, 2);
|
||||
utils::hook::set<uint8_t>(0x550A36_b, 0xEB);
|
||||
|
||||
// ignore configstring mismatch
|
||||
utils::hook::set<uint8_t>(0x1402591C9, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x341261_b, 0xEB);
|
||||
|
||||
// ignore dw handle in SV_PacketEvent
|
||||
utils::hook::set<uint8_t>(0x1404898E2, 0xEB);
|
||||
utils::hook::call(0x1404898D6, &net_compare_address);
|
||||
utils::hook::set<uint8_t>(0x1CBC22_b, 0xEB);
|
||||
|
||||
// ignore dw handle in SV_FindClientByAddress
|
||||
utils::hook::set<uint8_t>(0x140488EFD, 0xEB);
|
||||
utils::hook::call(0x140488EF1, &net_compare_address);
|
||||
utils::hook::set<uint8_t>(0x1CB24D_b, 0xEB);
|
||||
|
||||
// ignore dw handle in SV_DirectConnect
|
||||
utils::hook::set<uint8_t>(0x140480C58, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x140480E6F, 0xEB);
|
||||
utils::hook::call(0x140480C4B, &net_compare_address);
|
||||
utils::hook::call(0x140480E62, &net_compare_address);
|
||||
utils::hook::set<uint8_t>(0x54DFE8_b, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x54E1FD_b, 0xEB);
|
||||
|
||||
// increase cl_maxpackets
|
||||
dvars::override::register_int("cl_maxpackets", 1000, 1, 1000, game::DVAR_FLAG_SAVED);
|
||||
@ -284,31 +278,31 @@ namespace network
|
||||
dvars::override::register_int("sv_remote_client_snapshot_msec", 33, 33, 100, game::DVAR_FLAG_NONE);
|
||||
|
||||
// ignore impure client
|
||||
utils::hook::jump(0x140481B58, 0x140481BEE);
|
||||
utils::hook::jump(0x54EDD3_b, 0x54EE69_b);
|
||||
|
||||
// don't send checksum
|
||||
utils::hook::set<uint8_t>(0x1404F6398, 0);
|
||||
utils::hook::set<uint8_t>(0x59E628_b, 0);
|
||||
|
||||
// don't read checksum
|
||||
utils::hook::set(0x1404F6620, 0xC301B0);
|
||||
utils::hook::set(0x59E8B0_b, 0xC301B0);
|
||||
|
||||
// don't try to reconnect client
|
||||
utils::hook::call(0x140480DFF, reconnect_migratated_client);
|
||||
utils::hook::nop(0x140480DDB, 4); // this crashes when reconnecting for some reason
|
||||
utils::hook::jump(0x54D220_b, reconnect_migratated_client);
|
||||
utils::hook::nop(0x54E168_b, 4); // this crashes when reconnecting for some reason
|
||||
|
||||
// allow server owner to modify net_port before the socket bind
|
||||
utils::hook::call(0x140512BE5, register_netport_stub);
|
||||
utils::hook::call(0x140512D20, register_netport_stub);
|
||||
utils::hook::call(0x5BD032_b, register_netport_stub);
|
||||
utils::hook::call(0x5BD3F0_b, register_netport_stub);
|
||||
|
||||
// increase allowed packet size
|
||||
const auto max_packet_size = 0x20000;
|
||||
utils::hook::set<int>(0x1404255F1, max_packet_size);
|
||||
utils::hook::set<int>(0x140425630, max_packet_size);
|
||||
utils::hook::set<int>(0x140425522, max_packet_size);
|
||||
utils::hook::set<int>(0x140425545, max_packet_size);
|
||||
utils::hook::set<int>(0x4F1ED1_b, max_packet_size);
|
||||
utils::hook::set<int>(0x4F1F10_b, max_packet_size);
|
||||
utils::hook::set<int>(0x4F1E02_b, max_packet_size);
|
||||
utils::hook::set<int>(0x4F1E25_b, max_packet_size);
|
||||
|
||||
// ignore built in "print" oob command and add in our own
|
||||
utils::hook::set<uint8_t>(0x14025280E, 0xEB);
|
||||
utils::hook::set<uint8_t>(0x12F817_b, 0xEB);
|
||||
on("print", [](const game::netadr_s&, const std::string_view& data)
|
||||
{
|
||||
const std::string message{data};
|
||||
@ -317,7 +311,7 @@ namespace network
|
||||
|
||||
// Use our own socket since the game's socket doesn't work with non localhost addresses
|
||||
// why? no idea
|
||||
utils::hook::jump(0x140512B40, create_socket);
|
||||
utils::hook::jump(0x5BD210_b, create_socket);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -71,11 +71,11 @@ namespace party
|
||||
perform_game_initialization();
|
||||
|
||||
// exit from virtuallobby
|
||||
utils::hook::invoke<void>(0x140256D40, 1);
|
||||
utils::hook::invoke<void>(0x13C9C0_b, 1);
|
||||
|
||||
// CL_ConnectFromParty
|
||||
char session_info[0x100] = {};
|
||||
utils::hook::invoke<void>(0x140251560, 0, session_info, &target, mapname.data(), gametype.data());
|
||||
utils::hook::invoke<void>(0x12DFF0_b, 0, session_info, &target, mapname.data(), gametype.data());
|
||||
}
|
||||
|
||||
std::string get_dvar_string(const std::string& dvar)
|
||||
@ -111,52 +111,43 @@ namespace party
|
||||
return false;
|
||||
}
|
||||
|
||||
void didyouknow_stub(const char* dvar_name, const char* string)
|
||||
const char* get_didyouknow_stub(void* table, int row, int column)
|
||||
{
|
||||
if (!party::sv_motd.empty())
|
||||
if (party::sv_motd.empty())
|
||||
{
|
||||
string = party::sv_motd.data();
|
||||
return utils::hook::invoke<const char*>(0x5A0AC0_b, table, row, column);
|
||||
}
|
||||
|
||||
// This function either does Dvar_SetString or Dvar_RegisterString for the given dvar
|
||||
utils::hook::invoke<void>(0x1404FB210, dvar_name, string);
|
||||
return utils::string::va("%s", party::sv_motd.data());
|
||||
}
|
||||
|
||||
void disconnect_stub()
|
||||
void disconnect()
|
||||
{
|
||||
if (!game::VirtualLobby_Loaded())
|
||||
{
|
||||
if (game::CL_IsCgameInitialized())
|
||||
{
|
||||
// CL_ForwardCommandToServer
|
||||
utils::hook::invoke<void>(0x140253480, 0, "disconnect");
|
||||
// CL_AddReliableCommand
|
||||
utils::hook::invoke<void>(0x12B810_b, 0, "disconnect");
|
||||
// CL_WritePacket
|
||||
utils::hook::invoke<void>(0x14024DB10, 0);
|
||||
utils::hook::invoke<void>(0x13D490_b, 0);
|
||||
}
|
||||
// CL_Disconnect
|
||||
utils::hook::invoke<void>(0x140252060, 0);
|
||||
utils::hook::invoke<void>(0x12F080_b, 0);
|
||||
}
|
||||
}
|
||||
|
||||
utils::hook::detour cldisconnect_hook;
|
||||
utils::hook::detour cl_disconnect_hook;
|
||||
|
||||
void cl_disconnect_stub(int a1)
|
||||
void cl_disconnect_stub(int showMainMenu) // possibly bool
|
||||
{
|
||||
party::clear_sv_motd();
|
||||
cldisconnect_hook.invoke<void>(a1);
|
||||
cl_disconnect_hook.invoke<void>(showMainMenu);
|
||||
}
|
||||
|
||||
const auto drop_reason_stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.mov(rdx, rdi);
|
||||
a.mov(ecx, 2);
|
||||
a.jmp(0x140251F78);
|
||||
});
|
||||
|
||||
void menu_error(const std::string& error)
|
||||
{
|
||||
utils::hook::invoke<void>(0x1400DACC0, error.data(), "MENU_NOTICE");
|
||||
utils::hook::set(0x142C1DA98, 1);
|
||||
utils::hook::invoke<void>(0x17D770_b, error.data(), "MENU_NOTICE");
|
||||
utils::hook::set(0x2ED2F78_b, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,9 +183,15 @@ namespace party
|
||||
int get_client_count()
|
||||
{
|
||||
auto count = 0;
|
||||
const auto* svs_clients = *game::mp::svs_clients;
|
||||
if (svs_clients == nullptr)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
|
||||
{
|
||||
if (game::mp::svs_clients[i].header.state >= 1)
|
||||
if (svs_clients[i].header.state >= 1)
|
||||
{
|
||||
++count;
|
||||
}
|
||||
@ -206,9 +203,15 @@ namespace party
|
||||
int get_bot_count()
|
||||
{
|
||||
auto count = 0;
|
||||
const auto* svs_clients = *game::mp::svs_clients;
|
||||
if (svs_clients == nullptr)
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
for (auto i = 0; i < *game::mp::svs_numclients; ++i)
|
||||
{
|
||||
if (game::mp::svs_clients[i].header.state >= 1 &&
|
||||
if (svs_clients[i].header.state >= 1 &&
|
||||
game::SV_BotIsBot(i))
|
||||
{
|
||||
++count;
|
||||
@ -282,7 +285,6 @@ namespace party
|
||||
}
|
||||
|
||||
console::info("Starting map: %s\n", mapname.data());
|
||||
|
||||
auto* gametype = game::Dvar_FindVar("g_gametype");
|
||||
if (gametype && gametype->current.string)
|
||||
{
|
||||
@ -317,21 +319,28 @@ namespace party
|
||||
return;
|
||||
}
|
||||
|
||||
// hook disconnect command function
|
||||
utils::hook::jump(0x1402521C7, disconnect_stub);
|
||||
|
||||
// detour CL_Disconnect to clear motd
|
||||
cldisconnect_hook.create(0x140252060, cl_disconnect_stub);
|
||||
cl_disconnect_hook.create(0x12F080_b, cl_disconnect_stub);
|
||||
|
||||
if (game::environment::is_mp())
|
||||
{
|
||||
// show custom drop reason
|
||||
utils::hook::nop(0x140251EFB, 13);
|
||||
utils::hook::jump(0x140251EFB, drop_reason_stub, true);
|
||||
utils::hook::nop(0x12EF4E_b, 13);
|
||||
utils::hook::jump(0x12EF4E_b, utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.mov(rdx, rsi);
|
||||
a.mov(ecx, 2);
|
||||
a.jmp(0x12EF27_b);
|
||||
}), true);
|
||||
|
||||
command::add("disconnect", disconnect);
|
||||
}
|
||||
|
||||
// enable custom kick reason in GScr_KickPlayer
|
||||
utils::hook::set<uint8_t>(0x140376A1D, 0xEB);
|
||||
utils::hook::set<uint8_t>(0xE423D_b, 0xEB);
|
||||
|
||||
// allow custom didyouknow based on sv_motd
|
||||
utils::hook::call(0x1A8A3A_b, get_didyouknow_stub);
|
||||
|
||||
command::add("map", [](const command::params& argument)
|
||||
{
|
||||
@ -349,11 +358,12 @@ namespace party
|
||||
{
|
||||
return;
|
||||
}
|
||||
*reinterpret_cast<int*>(0x14A3A91D0) = 1; // sv_map_restart
|
||||
*reinterpret_cast<int*>(0x14A3A91D4) = 1; // sv_loadScripts
|
||||
*reinterpret_cast<int*>(0x14A3A91D8) = 0; // sv_migrate
|
||||
|
||||
utils::hook::invoke<void>(0x14047E7F0); // SV_CheckLoadGame
|
||||
*reinterpret_cast<int*>(0xB7B8E60_b) = 1; // sv_map_restart
|
||||
*reinterpret_cast<int*>(0xB7B8E64_b) = 1; // sv_loadScripts
|
||||
*reinterpret_cast<int*>(0xB7B8E68_b) = 0; // sv_migrate
|
||||
|
||||
utils::hook::invoke<void>(0x54BD50_b); // SV_CheckLoadGame
|
||||
});
|
||||
|
||||
command::add("fast_restart", []()
|
||||
@ -546,8 +556,6 @@ namespace party
|
||||
printf("%s\n", message.data());
|
||||
});
|
||||
|
||||
utils::hook::call(0x1404C6E8D, didyouknow_stub); // allow custom didyouknow based on sv_motd
|
||||
|
||||
network::on("getInfo", [](const game::netadr_s& target, const std::string_view& data)
|
||||
{
|
||||
utils::info_string info{};
|
||||
|
@ -46,7 +46,7 @@ namespace patches
|
||||
return "Unknown Soldier";
|
||||
}
|
||||
|
||||
return std::string{ username, username_len - 1 };
|
||||
return std::string{username, username_len - 1};
|
||||
}
|
||||
|
||||
utils::hook::detour com_register_dvars_hook;
|
||||
@ -57,23 +57,21 @@ namespace patches
|
||||
{
|
||||
// Make name save
|
||||
dvars::register_string("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name.");
|
||||
|
||||
// Disable data validation error popup
|
||||
dvars::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE, "");
|
||||
}
|
||||
|
||||
return com_register_dvars_hook.invoke<void>();
|
||||
}
|
||||
|
||||
void set_client_dvar_from_server_stub(void* a1, void* a2, const char* dvar, const char* value)
|
||||
utils::hook::detour set_client_dvar_from_server_hook;
|
||||
|
||||
void set_client_dvar_from_server_stub(void* clientNum, void* cgameGlob, const char* dvar, const char* value)
|
||||
{
|
||||
if (dvar == "cg_fov"s || dvar == "cg_fovMin"s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// CG_SetClientDvarFromServer
|
||||
utils::hook::invoke<void>(0x140236120, a1, a2, dvar, value);
|
||||
set_client_dvar_from_server_hook.invoke<void>(0x11AA90_b, clientNum, cgameGlob, dvar, value);
|
||||
}
|
||||
|
||||
const char* db_read_raw_file_stub(const char* filename, char* buf, const int size)
|
||||
@ -92,7 +90,7 @@ namespace patches
|
||||
}
|
||||
|
||||
// DB_ReadRawFile
|
||||
return utils::hook::invoke<const char*>(SELECT_VALUE(0x1401CD4F0, 0x1402BEF10), filename, buf, size);
|
||||
return utils::hook::invoke<const char*>(SELECT_VALUE(0x1F4D00_b, 0x3994B0_b), filename, buf, size);
|
||||
}
|
||||
|
||||
void bsp_sys_error_stub(const char* error, const char* arg1)
|
||||
@ -114,9 +112,15 @@ namespace patches
|
||||
utils::hook::detour cmd_lui_notify_server_hook;
|
||||
void cmd_lui_notify_server_stub(game::mp::gentity_s* ent)
|
||||
{
|
||||
const auto svs_clients = *game::mp::svs_clients;
|
||||
if (svs_clients == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
command::params_sv params{};
|
||||
const auto menu_id = atoi(params.get(1));
|
||||
const auto client = &game::mp::svs_clients[ent->s.entityNum];
|
||||
const auto client = &svs_clients[ent->s.entityNum];
|
||||
|
||||
// 22 => "end_game"
|
||||
if (menu_id == 22 && client->header.remoteAddress.type != game::NA_LOOPBACK)
|
||||
@ -138,15 +142,17 @@ namespace patches
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::invoke<void>(0x140481A00, client, msg);
|
||||
utils::hook::invoke<void>(0x54EC50_b, client, msg);
|
||||
}
|
||||
|
||||
void aim_assist_add_to_target_list(void* a1, void* a2)
|
||||
void aim_assist_add_to_target_list(void* aaGlob, void* screenTarget)
|
||||
{
|
||||
if (!dvars::aimassist_enabled->current.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
game::AimAssist_AddToTargetList(a1, a2);
|
||||
game::AimAssist_AddToTargetList(aaGlob, screenTarget);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,15 +162,15 @@ namespace patches
|
||||
void post_unpack() override
|
||||
{
|
||||
// Register dvars
|
||||
com_register_dvars_hook.create(SELECT_VALUE(0x140351B80, 0x1400D9320), &com_register_dvars_stub);
|
||||
com_register_dvars_hook.create(SELECT_VALUE(0x385BE0_b, 0x15BB60_b), &com_register_dvars_stub);
|
||||
|
||||
// Unlock fps in main menu
|
||||
utils::hook::set<BYTE>(SELECT_VALUE(0x14018D47B, 0x14025B86B), 0xEB);
|
||||
utils::hook::set<BYTE>(SELECT_VALUE(0x1B1EAB_b, 0x34396B_b), 0xEB);
|
||||
|
||||
if (!game::environment::is_dedi())
|
||||
{
|
||||
// Fix mouse lag
|
||||
utils::hook::nop(SELECT_VALUE(0x1403E3C05, 0x1404DB1AF), 6);
|
||||
utils::hook::nop(SELECT_VALUE(0x4631F9_b, 0x5BFF89_b), 6);
|
||||
scheduler::loop([]()
|
||||
{
|
||||
SetThreadExecutionState(ES_DISPLAY_REQUIRED);
|
||||
@ -177,11 +183,11 @@ namespace patches
|
||||
dvars::override::register_float("cg_fovMin", 1.f, 1.0f, 90.f, game::DvarFlags::DVAR_FLAG_SAVED);
|
||||
|
||||
// Allow kbam input when gamepad is enabled
|
||||
utils::hook::nop(SELECT_VALUE(0x14018797E, 0x14024EF60), 2);
|
||||
utils::hook::nop(SELECT_VALUE(0x1401856DC, 0x14024C6B0), 6);
|
||||
utils::hook::nop(SELECT_VALUE(0x1AC0CE_b, 0x135EFB_b), 2);
|
||||
utils::hook::nop(SELECT_VALUE(0x1A9DDC_b, 0x13388F_b), 6);
|
||||
|
||||
// Allow executing custom cfg files with the "exec" command
|
||||
utils::hook::call(SELECT_VALUE(0x140343855, 0x140403E28), db_read_raw_file_stub);
|
||||
utils::hook::call(SELECT_VALUE(0x376EB5_b, 0x156D41_b), db_read_raw_file_stub);
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
@ -191,56 +197,54 @@ namespace patches
|
||||
|
||||
static void patch_mp()
|
||||
{
|
||||
// Use name dvar
|
||||
utils::hook::jump(0x14050FF90, &live_get_local_client_name);
|
||||
utils::hook::jump(0x5BB9C0_b, &live_get_local_client_name);
|
||||
|
||||
// Disable data validation error popup
|
||||
dvars::override::register_int("data_validation_allow_drop", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
|
||||
// Patch SV_KickClientNum
|
||||
sv_kick_client_num_hook.create(0x14047ED00, &sv_kick_client_num);
|
||||
sv_kick_client_num_hook.create(game::SV_KickClientNum, &sv_kick_client_num);
|
||||
|
||||
// block changing name in-game
|
||||
utils::hook::set<uint8_t>(0x14047FC90, 0xC3);
|
||||
|
||||
// patch "Couldn't find the bsp for this map." error to not be fatal in mp
|
||||
utils::hook::call(0x1402BA26B, bsp_sys_error_stub);
|
||||
utils::hook::set<uint8_t>(0x54CFF0_b, 0xC3);
|
||||
|
||||
// client side aim assist dvar
|
||||
dvars::aimassist_enabled = dvars::register_bool("aimassist_enabled", true,
|
||||
game::DvarFlags::DVAR_FLAG_SAVED,
|
||||
"Enables aim assist for controllers");
|
||||
utils::hook::call(0x14009EE9E, aim_assist_add_to_target_list);
|
||||
utils::hook::call(0xE857F_b, aim_assist_add_to_target_list);
|
||||
|
||||
// patch "Couldn't find the bsp for this map." error to not be fatal in mp
|
||||
utils::hook::call(0x39465B_b, bsp_sys_error_stub);
|
||||
|
||||
// isProfanity
|
||||
utils::hook::set(0x1402877D0, 0xC3C033);
|
||||
|
||||
// disable emblems
|
||||
dvars::override::register_int("emblems_active", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
utils::hook::set<uint8_t>(0x140479590, 0xC3); // don't register commands
|
||||
utils::hook::set(0x361AA0_b, 0xC3C033);
|
||||
|
||||
// disable elite_clan
|
||||
dvars::override::register_int("elite_clan_active", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
utils::hook::set<uint8_t>(0x140585680, 0xC3); // don't register commands
|
||||
utils::hook::set<uint8_t>(0x62D2F0_b, 0xC3); // don't register commands
|
||||
|
||||
// disable codPointStore
|
||||
dvars::override::register_int("codPointStore_enabled", 0, 0, 0, game::DVAR_FLAG_NONE);
|
||||
|
||||
// don't register every replicated dvar as a network dvar
|
||||
utils::hook::nop(0x14039E58E, 5); // dvar_foreach
|
||||
utils::hook::nop(0x47408E_b, 5); // dvar_foreach
|
||||
|
||||
// patch "Server is different version" to show the server client version
|
||||
utils::hook::inject(0x140480955, VERSION);
|
||||
utils::hook::inject(0x54DCE5_b, VERSION);
|
||||
|
||||
// prevent servers overriding our fov
|
||||
utils::hook::call(0x14023279E, set_client_dvar_from_server_stub);
|
||||
utils::hook::nop(0x1400DAF69, 5);
|
||||
utils::hook::nop(0x140190C16, 5);
|
||||
utils::hook::set<uint8_t>(0x14021D22A, 0xEB);
|
||||
set_client_dvar_from_server_hook.create(0x11AA90_b, set_client_dvar_from_server_stub);
|
||||
utils::hook::nop(0x17DA96_b, 0x16);
|
||||
utils::hook::nop(0xE00BE_b, 0x17);
|
||||
utils::hook::set<uint8_t>(0x307F39_b, 0xEB);
|
||||
|
||||
// some anti tamper thing that kills performance
|
||||
// some [data validation] anti tamper thing that kills performance
|
||||
dvars::override::register_int("dvl", 0, 0, 0, game::DVAR_FLAG_READ);
|
||||
|
||||
// unlock safeArea_*
|
||||
utils::hook::jump(0x1402624F5, 0x140262503);
|
||||
utils::hook::jump(0x14026251C, 0x140262547);
|
||||
utils::hook::jump(0x347BC5_b, 0x347BD3_b);
|
||||
utils::hook::jump(0x347BEC_b, 0x347C17_b);
|
||||
dvars::override::register_float("safeArea_adjusted_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
|
||||
dvars::override::register_float("safeArea_adjusted_vertical", 1, 0, 1, game::DVAR_FLAG_SAVED);
|
||||
dvars::override::register_float("safeArea_horizontal", 1, 0, 1, game::DVAR_FLAG_SAVED);
|
||||
@ -262,23 +266,26 @@ namespace patches
|
||||
dvars::override::register_int("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED);
|
||||
|
||||
// Prevent clients from ending the game as non host by sending 'end_game' lui notification
|
||||
cmd_lui_notify_server_hook.create(0x140335A70, cmd_lui_notify_server_stub);
|
||||
cmd_lui_notify_server_hook.create(0x412D50_b, cmd_lui_notify_server_stub);
|
||||
|
||||
// Prevent clients from sending invalid reliableAcknowledge
|
||||
utils::hook::call(0x1404899C6, sv_execute_client_message_stub);
|
||||
utils::hook::call(0x1CBD06_b, sv_execute_client_message_stub);
|
||||
|
||||
// "fix" for rare 'Out of memory error' error
|
||||
if (utils::flags::has_flag("memoryfix"))
|
||||
{
|
||||
utils::hook::jump(0x140578BE0, malloc);
|
||||
utils::hook::jump(0x140578B00, _aligned_malloc);
|
||||
utils::hook::jump(0x140578C40, free);
|
||||
utils::hook::jump(0x140578D30, realloc);
|
||||
utils::hook::jump(0x140578B60, _aligned_realloc);
|
||||
utils::hook::jump(0x6200C0_b, malloc);
|
||||
utils::hook::jump(0x61FFE0_b, _aligned_malloc);
|
||||
utils::hook::jump(0x620120_b, free);
|
||||
utils::hook::jump(0x620210_b, realloc);
|
||||
utils::hook::jump(0x620040_b, _aligned_realloc);
|
||||
}
|
||||
|
||||
// Change default hostname and make it replicated
|
||||
dvars::override::register_string("sv_hostname", "^2H1-Mod^7 Default Server", game::DVAR_FLAG_REPLICATED);
|
||||
|
||||
// Dont free server/client memory on asset loading (fixes crashing on map rotation)
|
||||
utils::hook::nop(0x132474_b, 5);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -27,19 +27,17 @@ namespace ranked
|
||||
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED);
|
||||
}
|
||||
|
||||
if (game::environment::is_dedi() && !utils::flags::has_flag("unranked"))
|
||||
if (game::environment::is_dedi())
|
||||
{
|
||||
dvars::override::register_bool("xblive_privatematch", false, game::DVAR_FLAG_REPLICATED | game::DVAR_FLAG_WRITE);
|
||||
|
||||
// Skip some check in _menus.gsc
|
||||
dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, "");
|
||||
dvars::register_bool("force_ranking", true, game::DVAR_FLAG_WRITE, ""); // Skip some check in _menus.gsc
|
||||
}
|
||||
|
||||
// Always run bots, even if xblive_privatematch is 0
|
||||
utils::hook::set(0x1401D9300, 0xC301B0); // BG_BotSystemEnabled
|
||||
utils::hook::set(0x1401D90D0, 0xC301B0); // BG_AISystemEnabled
|
||||
utils::hook::set(0x1401D92A0, 0xC301B0); // BG_BotFastFileEnabled
|
||||
utils::hook::set(0x1401D9400, 0xC301B0); // BG_BotsUsingTeamDifficulty
|
||||
utils::hook::set(0x2C10B0_b, 0xC301B0); // BG_BotSystemEnabled
|
||||
utils::hook::set(0x2C0E60_b, 0xC301B0); // BG_AISystemEnabled
|
||||
utils::hook::set(0x2C1040_b, 0xC301B0); // BG_BotFastFileEnabled
|
||||
utils::hook::set(0x2C11B0_b, 0xC301B0); // BG_BotsUsingTeamDifficulty
|
||||
}
|
||||
};
|
||||
}
|
||||
|
232
src/client/component/rcon.cpp
Normal file
232
src/client/component/rcon.cpp
Normal file
@ -0,0 +1,232 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
#include "rcon.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace rcon
|
||||
{
|
||||
namespace
|
||||
{
|
||||
bool is_redirecting_ = false;
|
||||
bool has_redirected_ = false;
|
||||
game::netadr_s redirect_target_ = {};
|
||||
std::recursive_mutex redirect_lock;
|
||||
|
||||
void setup_redirect(const game::netadr_s& target)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||
|
||||
has_redirected_ = false;
|
||||
is_redirecting_ = true;
|
||||
redirect_target_ = target;
|
||||
}
|
||||
|
||||
void clear_redirect()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||
|
||||
has_redirected_ = false;
|
||||
is_redirecting_ = false;
|
||||
redirect_target_ = {};
|
||||
}
|
||||
|
||||
void send_rcon_command(const std::string& password, const std::string& data)
|
||||
{
|
||||
// If you are the server, don't bother with rcon and just execute the command
|
||||
if (game::Dvar_FindVar("sv_running")->current.enabled)
|
||||
{
|
||||
game::Cbuf_AddText(0, 0, data.data());
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.empty())
|
||||
{
|
||||
console::info("You must login first to use RCON\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (*game::mp::connect_state != nullptr && *game::connectionState >= game::CA_CONNECTED)
|
||||
{
|
||||
const auto target = (*game::mp::connect_state)->address;
|
||||
const auto buffer = password + " " + data;
|
||||
network::send(target, "rcon", buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
console::warn("You need to be connected to a server!\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::string build_status_buffer()
|
||||
{
|
||||
const auto sv_maxclients = game::Dvar_FindVar("sv_maxclients");
|
||||
const auto mapname = game::Dvar_FindVar("mapname");
|
||||
|
||||
std::string buffer{};
|
||||
buffer.append(utils::string::va("map: %s\n", mapname->current.string));
|
||||
buffer.append(
|
||||
"num score bot ping guid name address qport\n");
|
||||
buffer.append(
|
||||
"--- ----- --- ---- -------------------------------- ---------------- --------------------- -----\n");
|
||||
|
||||
const auto svs_clients = *game::mp::svs_clients;
|
||||
if (svs_clients == nullptr)
|
||||
{
|
||||
return buffer;
|
||||
}
|
||||
|
||||
for (int i = 0; i < sv_maxclients->current.integer; i++)
|
||||
{
|
||||
const auto client = &svs_clients[i];
|
||||
|
||||
char clean_name[32] = {0};
|
||||
strncpy_s(clean_name, client->name, sizeof(clean_name));
|
||||
game::I_CleanStr(clean_name);
|
||||
|
||||
if (client->header.state >= 1)
|
||||
{
|
||||
buffer.append(utils::string::va("%3i %5i %3s %s %32s %16s %21s %5i\n",
|
||||
i,
|
||||
game::G_GetClientScore(i),
|
||||
game::SV_BotIsBot(i) ? "Yes" : "No",
|
||||
(client->header.state == 2)
|
||||
? "CNCT"
|
||||
: (client->header.state == 1)
|
||||
? "ZMBI"
|
||||
: utils::string::va("%4i", game::SV_GetClientPing(i)),
|
||||
game::SV_GetGuid(i),
|
||||
clean_name,
|
||||
network::net_adr_to_string(client->header.remoteAddress),
|
||||
client->header.remoteAddress.port)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
|
||||
bool message_redirect(const std::string& message)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> $(redirect_lock);
|
||||
|
||||
if (is_redirecting_)
|
||||
{
|
||||
has_redirected_ = true;
|
||||
network::send(redirect_target_, "print", message, '\n');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler::once([]()
|
||||
{
|
||||
dvars::register_string("rcon_password", "", game::DvarFlags::DVAR_FLAG_NONE,
|
||||
"The password for remote console");
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
command::add("status", []()
|
||||
{
|
||||
const auto sv_running = game::Dvar_FindVar("sv_running");
|
||||
if (game::VirtualLobby_Loaded() || !sv_running || !sv_running->current.enabled)
|
||||
{
|
||||
console::error("Server is not running\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto status_buffer = build_status_buffer();
|
||||
console::info(status_buffer.data());
|
||||
});
|
||||
|
||||
if (!game::environment::is_dedi())
|
||||
{
|
||||
command::add("rcon", [&](const command::params& params)
|
||||
{
|
||||
static std::string rcon_password{};
|
||||
|
||||
if (params.size() < 2) return;
|
||||
|
||||
const auto operation = params.get(1);
|
||||
if (operation == "login"s)
|
||||
{
|
||||
if (params.size() < 3) return;
|
||||
|
||||
rcon_password = params.get(2);
|
||||
}
|
||||
else if (operation == "logout"s)
|
||||
{
|
||||
rcon_password.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
send_rcon_command(rcon_password, params.join(1));
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
network::on("rcon", [](const game::netadr_s& addr, const std::string_view& data)
|
||||
{
|
||||
const auto message = std::string{data};
|
||||
const auto pos = message.find_first_of(" ");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
network::send(addr, "print", "Invalid RCon request", '\n');
|
||||
console::info("Invalid RCon request from %s\n", network::net_adr_to_string(addr));
|
||||
return;
|
||||
}
|
||||
|
||||
const auto password = message.substr(0, pos);
|
||||
const auto command = message.substr(pos + 1);
|
||||
const auto rcon_password = game::Dvar_FindVar("rcon_password");
|
||||
if (command.empty() || !rcon_password || !rcon_password->current.string || !strlen(
|
||||
rcon_password->current.string))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setup_redirect(addr);
|
||||
|
||||
if (password != rcon_password->current.string)
|
||||
{
|
||||
network::send(redirect_target_, "print", "Invalid rcon password", '\n');
|
||||
console::error("Invalid rcon password\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
command::execute(command, true);
|
||||
}
|
||||
|
||||
if (!has_redirected_)
|
||||
{
|
||||
network::send(redirect_target_, "print", "", '\n');
|
||||
}
|
||||
|
||||
clear_redirect();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(rcon::component)
|
6
src/client/component/rcon.hpp
Normal file
6
src/client/component/rcon.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace rcon
|
||||
{
|
||||
bool message_redirect(const std::string& message);
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game_module.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include "game_module.hpp"
|
||||
|
||||
namespace redirect
|
||||
{
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "dvars.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
@ -30,7 +33,6 @@ namespace renderer
|
||||
void gfxdrawmethod()
|
||||
{
|
||||
game::gfxDrawMethod->drawScene = game::GFX_DRAW_SCENE_STANDARD;
|
||||
|
||||
game::gfxDrawMethod->baseTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_LIT;
|
||||
game::gfxDrawMethod->emissiveTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : game::TECHNIQUE_EMISSIVE;
|
||||
game::gfxDrawMethod->forceTechType = dvars::r_fullbright->current.enabled ? get_fullbright_technique() : 242;
|
||||
@ -67,19 +69,15 @@ namespace renderer
|
||||
|
||||
dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 4, game::DVAR_FLAG_SAVED, "Toggles rendering without lighting");
|
||||
|
||||
r_init_draw_method_hook.create(SELECT_VALUE(0x1404BD140, 0x1405C46E0), &r_init_draw_method_stub);
|
||||
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x1404F8870, 0x1405FF9E0), &r_update_front_end_dvar_options_stub);
|
||||
r_init_draw_method_hook.create(SELECT_VALUE(0x5467E0_b, 0x669580_b), &r_init_draw_method_stub);
|
||||
r_update_front_end_dvar_options_hook.create(SELECT_VALUE(0x583560_b, 0x6A78C0_b), &r_update_front_end_dvar_options_stub);
|
||||
|
||||
// use "saved" flags for "r_normalMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF5CA, 0x1405D460E), game::DVAR_FLAG_SAVED);
|
||||
|
||||
// use "saved" flags for "r_specularMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF5F5, 0x1405D4639), game::DVAR_FLAG_SAVED);
|
||||
|
||||
// use "saved" flags for "r_specOccMap"
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x1404CF620, 0x1405D4664), game::DVAR_FLAG_SAVED);
|
||||
// use "saved" flags
|
||||
dvars::override::register_enum("r_normalMap", game::DVAR_FLAG_SAVED);
|
||||
dvars::override::register_enum("r_specularMap", game::DVAR_FLAG_SAVED);
|
||||
dvars::override::register_enum("r_specOccMap", game::DVAR_FLAG_SAVED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(renderer::component)
|
||||
REGISTER_COMPONENT(renderer::component)
|
||||
|
@ -75,7 +75,8 @@ namespace scheduler
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
@ -99,7 +100,6 @@ namespace scheduler
|
||||
void r_end_frame_stub()
|
||||
{
|
||||
execute(pipeline::renderer);
|
||||
r_end_frame_hook.invoke<void>();
|
||||
}
|
||||
|
||||
void server_frame_stub()
|
||||
@ -108,10 +108,14 @@ namespace scheduler
|
||||
execute(pipeline::server);
|
||||
}
|
||||
|
||||
void main_frame_stub()
|
||||
void* main_frame_stub()
|
||||
{
|
||||
main_frame_hook.invoke<void>();
|
||||
execute(pipeline::main);
|
||||
const auto _0 = gsl::finally([]()
|
||||
{
|
||||
execute(pipeline::main);
|
||||
});
|
||||
|
||||
return main_frame_hook.invoke<void*>();
|
||||
}
|
||||
|
||||
void hks_frame_stub()
|
||||
@ -121,11 +125,12 @@ namespace scheduler
|
||||
{
|
||||
execute(pipeline::lui);
|
||||
}
|
||||
hks_frame_hook.invoke<bool>();
|
||||
}
|
||||
}
|
||||
|
||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
assert(type >= 0 && type < pipeline::count);
|
||||
|
||||
@ -138,7 +143,7 @@ namespace scheduler
|
||||
}
|
||||
|
||||
void loop(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
{
|
||||
@ -148,7 +153,7 @@ namespace scheduler
|
||||
}
|
||||
|
||||
void once(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([callback]()
|
||||
{
|
||||
@ -158,7 +163,7 @@ namespace scheduler
|
||||
}
|
||||
|
||||
void on_game_initialized(const std::function<void()>& callback, const pipeline type,
|
||||
const std::chrono::milliseconds delay)
|
||||
const std::chrono::milliseconds delay)
|
||||
{
|
||||
schedule([=]()
|
||||
{
|
||||
@ -190,10 +195,22 @@ namespace scheduler
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
r_end_frame_hook.create(SELECT_VALUE(0x1404F7310, 0x1405FE470), scheduler::r_end_frame_stub);
|
||||
g_run_frame_hook.create(SELECT_VALUE(0x1402772D0, 0x14033A640), scheduler::server_frame_stub);
|
||||
main_frame_hook.create(SELECT_VALUE(0x1401CE8D0, 0x1400D8310), scheduler::main_frame_stub);
|
||||
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), scheduler::hks_frame_stub);
|
||||
utils::hook::jump(SELECT_VALUE(0x581FB0_b, 0x6A6300_b), utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.pushad64();
|
||||
a.call_aligned(r_end_frame_stub);
|
||||
a.popad64();
|
||||
|
||||
a.sub(rsp, 0x28);
|
||||
a.call(SELECT_VALUE(0x581840_b, 0x6A5C20_b));
|
||||
a.mov(rax, SELECT_VALUE(0x1182A680_b, 0xEAB4308_b));
|
||||
a.mov(rax, qword_ptr(rax));
|
||||
a.jmp(SELECT_VALUE(0x581FC0_b, 0x6A6310_b));
|
||||
}), true);
|
||||
|
||||
g_run_frame_hook.create(SELECT_VALUE(0x2992E0_b, 0x417940_b), scheduler::server_frame_stub);
|
||||
main_frame_hook.create(SELECT_VALUE(0x1B1DF0_b, 0x3438B0_b), scheduler::main_frame_stub);
|
||||
hks_frame_hook.create(SELECT_VALUE(0x1028D0_b, 0x2792E0_b), scheduler::hks_frame_stub);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
@ -207,4 +224,4 @@ namespace scheduler
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(scheduler::component)
|
||||
REGISTER_COMPONENT(scheduler::component)
|
@ -94,6 +94,7 @@ namespace scripting
|
||||
script_function_table.clear();
|
||||
}
|
||||
|
||||
scripting::notify(*game::levelEntityId, "shutdownGame_called", {1});
|
||||
lua::engine::stop();
|
||||
return g_shutdown_game_hook.invoke<void>(free_scripts);
|
||||
}
|
||||
@ -159,26 +160,6 @@ namespace scripting
|
||||
scripting::token_map[str] = result;
|
||||
return result;
|
||||
}
|
||||
|
||||
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
|
||||
{
|
||||
const auto result = db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
||||
if (!g_dump_scripts->current.enabled || type != game::XAssetType::ASSET_TYPE_SCRIPTFILE)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string buffer;
|
||||
buffer.append(result.scriptfile->name, strlen(result.scriptfile->name) + 1);
|
||||
buffer.append(reinterpret_cast<char*>(&result.scriptfile->compressedLen), 4);
|
||||
buffer.append(reinterpret_cast<char*>(&result.scriptfile->len), 4);
|
||||
buffer.append(reinterpret_cast<char*>(&result.scriptfile->bytecodeLen), 4);
|
||||
buffer.append(result.scriptfile->buffer, result.scriptfile->compressedLen);
|
||||
buffer.append(result.scriptfile->bytecode, result.scriptfile->bytecodeLen);
|
||||
utils::io::write_file(utils::string::va("gsc_dump/%s.gscbin", name), buffer);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -186,27 +167,24 @@ namespace scripting
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
vm_notify_hook.create(SELECT_VALUE(0x140379A00, 0x1404479F0), vm_notify_stub);
|
||||
vm_notify_hook.create(SELECT_VALUE(0x3CD500_b, 0x514560_b), vm_notify_stub);
|
||||
|
||||
scr_add_class_field_hook.create(SELECT_VALUE(0x140370370, 0x14043E2C0), scr_add_class_field_stub);
|
||||
scr_add_class_field_hook.create(SELECT_VALUE(0x3C3CE0_b, 0x50AE20_b), scr_add_class_field_stub);
|
||||
|
||||
scr_set_thread_position_hook.create(SELECT_VALUE(0x14036A180, 0x140437D10), scr_set_thread_position_stub);
|
||||
process_script_hook.create(SELECT_VALUE(0x1403737E0, 0x1404417E0), process_script_stub);
|
||||
scr_set_thread_position_hook.create(SELECT_VALUE(0x3BD890_b, 0x504870_b), scr_set_thread_position_stub);
|
||||
process_script_hook.create(SELECT_VALUE(0x3C7200_b, 0x50E340_b), process_script_stub);
|
||||
sl_get_canonical_string_hook.create(game::SL_GetCanonicalString, sl_get_canonical_string_stub);
|
||||
|
||||
if (!game::environment::is_sp())
|
||||
{
|
||||
scr_load_level_hook.create(SELECT_VALUE(0x1402A5BE0, 0x1403727C0), scr_load_level_stub);
|
||||
scr_load_level_hook.create(0x450FC0_b, scr_load_level_stub);
|
||||
}
|
||||
else
|
||||
{
|
||||
vm_execute_hook.create(SELECT_VALUE(0x140376590, 0x140444580), vm_execute_stub);
|
||||
vm_execute_hook.create(0x3CA080_b, vm_execute_stub);
|
||||
}
|
||||
|
||||
g_shutdown_game_hook.create(SELECT_VALUE(0x140277D40, 0x140345A60), g_shutdown_game_stub);
|
||||
|
||||
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
|
||||
g_dump_scripts = dvars::register_bool("g_dumpScripts", false, game::DVAR_FLAG_NONE, "Dump GSC scripts");
|
||||
g_shutdown_game_hook.create(SELECT_VALUE(0x2A5130_b, 0x422F30_b), g_shutdown_game_stub);
|
||||
|
||||
scheduler::loop([]()
|
||||
{
|
||||
|
@ -118,8 +118,7 @@ namespace server_list
|
||||
return diff > server_limit ? server_limit : static_cast<int>(diff);
|
||||
}
|
||||
|
||||
const char* ui_feeder_item_text(int /*localClientNum*/, void* /*a2*/, void* /*a3*/, const int index,
|
||||
const int column)
|
||||
const char* ui_feeder_item_text(const int index, const int column)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(mutex);
|
||||
|
||||
@ -260,18 +259,18 @@ namespace server_list
|
||||
|
||||
utils::hook::detour lui_open_menu_hook;
|
||||
|
||||
void lui_open_menu_stub(int controllerIndex, const char* menu, int a3, int a4, unsigned int a5)
|
||||
void lui_open_menu_stub(int controllerIndex, const char* menuName, int isPopup, int isModal, unsigned int isExclusive)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
console::info("[LUI] %s\n", menu);
|
||||
console::info("[LUI] %s\n", menuName);
|
||||
#endif
|
||||
|
||||
if (!strcmp(menu, "menu_systemlink_join"))
|
||||
if (!strcmp(menuName, "menu_systemlink_join"))
|
||||
{
|
||||
refresh_server_list();
|
||||
}
|
||||
|
||||
lui_open_menu_hook.invoke<void>(controllerIndex, menu, a3, a4, a5);
|
||||
lui_open_menu_hook.invoke<void>(controllerIndex, menuName, isPopup, isModal, isExclusive);
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,6 +320,11 @@ namespace server_list
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.get("gamename") != "H1")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int start_time{};
|
||||
const auto now = game::Sys_Milliseconds();
|
||||
|
||||
@ -366,13 +370,64 @@ namespace server_list
|
||||
lui_open_menu_hook.create(game::LUI_OpenMenu, lui_open_menu_stub);
|
||||
|
||||
// replace UI_RunMenuScript call in LUI_CoD_LuaCall_RefreshServerList to our refresh_servers
|
||||
utils::hook::call(0x14018A0C9, &refresh_server_list);
|
||||
utils::hook::call(0x14018A5DE, &join_server);
|
||||
utils::hook::nop(0x14018A5FD, 5);
|
||||
utils::hook::jump(0x28E049_b, utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.pushad64();
|
||||
a.call_aligned(refresh_server_list);
|
||||
a.popad64();
|
||||
|
||||
a.xor_(eax, eax);
|
||||
a.mov(rbx, qword_ptr(rsp, 0x38));
|
||||
a.add(rsp, 0x20);
|
||||
a.pop(rdi);
|
||||
a.ret();
|
||||
}), true);
|
||||
|
||||
utils::hook::jump(0x28E557_b, utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.mov(r8d, edi);
|
||||
a.mov(ecx, eax);
|
||||
a.mov(ebx, eax);
|
||||
|
||||
a.pushad64();
|
||||
a.call_aligned(join_server);
|
||||
a.popad64();
|
||||
|
||||
a.jmp(0x28E563_b);
|
||||
}), true);
|
||||
|
||||
utils::hook::nop(0x28E57D_b, 5);
|
||||
|
||||
// do feeder stuff
|
||||
utils::hook::call(0x14018A199, &ui_feeder_count);
|
||||
utils::hook::call(0x14018A3B1, &ui_feeder_item_text);
|
||||
utils::hook::jump(0x28E117_b, utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.mov(ecx, eax);
|
||||
|
||||
a.pushad64();
|
||||
a.call_aligned(ui_feeder_count);
|
||||
a.movd(xmm0, eax);
|
||||
a.popad64();
|
||||
|
||||
a.mov(rax, qword_ptr(rbx, 0x48));
|
||||
a.cvtdq2ps(xmm0, xmm0);
|
||||
a.jmp(0x28E12B_b);
|
||||
}), true);
|
||||
|
||||
utils::hook::jump(0x28E331_b, utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
a.push(rax);
|
||||
a.pushad64();
|
||||
a.mov(rcx, r9); // index
|
||||
a.mov(rdx, qword_ptr(rsp, 0x88 + 0x20)); // column
|
||||
a.call_aligned(ui_feeder_item_text);
|
||||
a.mov(qword_ptr(rsp, 0x80), rax);
|
||||
a.popad64();
|
||||
a.pop(rax);
|
||||
|
||||
a.mov(rsi, qword_ptr(rsp, 0x90));
|
||||
a.mov(rdi, rax);
|
||||
a.jmp(0x28E341_b);
|
||||
}), true);
|
||||
|
||||
scheduler::loop(do_frame_work, scheduler::pipeline::main);
|
||||
|
||||
|
@ -1,51 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "dvars.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
namespace shaders
|
||||
{
|
||||
namespace
|
||||
{
|
||||
game::dvar_t* disable_shader_caching = nullptr;
|
||||
|
||||
bool shader_should_show_dialog_stub()
|
||||
{
|
||||
return !disable_shader_caching->current.enabled;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (!game::environment::is_mp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto has_flag = utils::flags::has_flag("noshadercaching");
|
||||
|
||||
disable_shader_caching = dvars::register_bool("disable_shader_caching", has_flag,
|
||||
game::DVAR_FLAG_SAVED, "Disable shader caching");
|
||||
if (has_flag)
|
||||
{
|
||||
dvars::override::set_bool("disable_shader_caching", 1);
|
||||
dvars::override::set_from_string("disable_shader_caching", "1");
|
||||
}
|
||||
|
||||
utils::hook::jump(0x14007E710, shader_should_show_dialog_stub);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(shaders::component)
|
@ -45,7 +45,7 @@ namespace slowmotion
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::jump(0x140365480, scr_cmd_set_slow_motion);
|
||||
utils::hook::jump(0x43D2E0_b, scr_cmd_set_slow_motion);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -30,9 +30,9 @@ namespace splash
|
||||
void post_unpack() override
|
||||
{
|
||||
// Disable native splash screen
|
||||
utils::hook::nop(SELECT_VALUE(0x1403E192E, 0x1405123E2), 5);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403E2E70, 0x140513AF0), destroy_stub);
|
||||
utils::hook::jump(SELECT_VALUE(0x1403E2EB0, 0x140513B30), destroy_stub);
|
||||
utils::hook::set<uint8_t>(SELECT_VALUE(0x462B90_b, 0x5BDF20_b), 0xC3);
|
||||
utils::hook::jump(SELECT_VALUE(0x462E40_b, 0x5BE1D0_b), destroy_stub, true);
|
||||
utils::hook::jump(SELECT_VALUE(0x462E80_b, 0x5BE210_b), destroy_stub, true);
|
||||
}
|
||||
|
||||
void pre_destroy() override
|
||||
|
@ -16,45 +16,35 @@ namespace stats
|
||||
namespace
|
||||
{
|
||||
game::dvar_t* cg_unlock_all_items;
|
||||
game::dvar_t* cg_unlock_all_loot;
|
||||
|
||||
utils::hook::detour is_item_unlocked_hook;
|
||||
utils::hook::detour is_item_unlocked_hook2;
|
||||
utils::hook::detour is_item_unlocked_hook3;
|
||||
|
||||
int is_item_unlocked_stub(int a1, void* a2, int a3)
|
||||
int is_item_unlocked_stub(int a1, void* a2, void* a3, void* a4, int a5, void* a6)
|
||||
{
|
||||
if (cg_unlock_all_items->current.enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return is_item_unlocked_hook.invoke<int>(a1, a2, a3);
|
||||
}
|
||||
|
||||
int is_item_unlocked_stub2(int a1, void* a2, void* a3, void* a4, int a5, void* a6)
|
||||
{
|
||||
if (cg_unlock_all_items->current.enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return is_item_unlocked_hook2.invoke<int>(a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
|
||||
int is_item_unlocked_stub3(int a1)
|
||||
{
|
||||
if (cg_unlock_all_items->current.enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return is_item_unlocked_hook3.invoke<int>(a1);
|
||||
return is_item_unlocked_hook.invoke<int>(a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
|
||||
int is_item_unlocked()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int is_item_unlocked_stub2(void* a1, void* a2)
|
||||
{
|
||||
const auto state = is_item_unlocked_hook2.invoke<int>(a1, a2);
|
||||
if (state == 15 /*Not In Inventory*/ && cg_unlock_all_loot->current.enabled)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -67,22 +57,25 @@ namespace stats
|
||||
return;
|
||||
}
|
||||
|
||||
utils::hook::jump(0x19E6E0_b, is_item_unlocked, true);
|
||||
|
||||
if (game::environment::is_dedi())
|
||||
{
|
||||
utils::hook::jump(0x140413E60, is_item_unlocked);
|
||||
utils::hook::jump(0x140413860, is_item_unlocked);
|
||||
utils::hook::jump(0x140412B70, is_item_unlocked);
|
||||
utils::hook::jump(0x19E070_b, is_item_unlocked, true);
|
||||
utils::hook::jump(0x19D390_b, is_item_unlocked, true);
|
||||
utils::hook::jump(0x19D140_b, is_item_unlocked, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
is_item_unlocked_hook.create(0x19E070_b, is_item_unlocked_stub);
|
||||
is_item_unlocked_hook2.create(0x19D140_b, is_item_unlocked_stub2);
|
||||
|
||||
cg_unlock_all_items = dvars::register_bool("cg_unlockall_items", false, game::DVAR_FLAG_SAVED,
|
||||
"Whether items should be locked based on the player's stats or always unlocked.");
|
||||
dvars::register_bool("cg_unlockall_classes", false, game::DVAR_FLAG_SAVED,
|
||||
"Whether classes should be locked based on the player's stats or always unlocked.");
|
||||
|
||||
is_item_unlocked_hook.create(0x140413E60, is_item_unlocked_stub);
|
||||
is_item_unlocked_hook2.create(0x140413860, is_item_unlocked_stub2);
|
||||
is_item_unlocked_hook3.create(0x140412B70, is_item_unlocked_stub3);
|
||||
cg_unlock_all_loot = dvars::register_bool("cg_unlockall_loot", false, game::DVAR_FLAG_SAVED,
|
||||
"Whether loot should be locked based on the player's stats or always unlocked.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -42,7 +42,7 @@ namespace steam_proxy
|
||||
#ifndef DEV_BUILD
|
||||
try
|
||||
{
|
||||
this->start_mod("\xF0\x9F\x90\xA4" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100);
|
||||
this->start_mod("\xF0\x9F\x8E\xAE" " H1-Mod: "s + (game::environment::is_sp() ? "Singleplayer" : "Multiplayer"), game::environment::is_sp() ? 393080 : 393100);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
|
@ -51,25 +51,26 @@ namespace system_check
|
||||
{
|
||||
static std::unordered_map<std::string, std::string> mp_zone_hashes =
|
||||
{
|
||||
{"patch_common_mp.ff", "3F44B0CFB0B8E0FBD9687C2942204AB7F11E66E6E15C73B8B4A5EB5920115A31"},
|
||||
{"patch_common_mp.ff", "E45EF5F29D12A5A47F405F89FBBEE479C0A90D02141ABF852D481689514134A1"},
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, std::string> sp_zone_hashes =
|
||||
{
|
||||
// Steam doesn't necessarily deliver this file :(
|
||||
{"patch_common.ff", "BB0617DD94AF2F511571E7184BBEDE76E64D97E5D0DAFDB457F00717F035EBF0"},
|
||||
{"patch_common.ff", "1D32A9770F90ED022AA76F4859B4AB178E194A703383E61AC2CE83B1E828B18F"},
|
||||
};
|
||||
|
||||
|
||||
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
|
||||
}
|
||||
|
||||
// need to update these values
|
||||
void verify_binary_version()
|
||||
{
|
||||
const auto value = *reinterpret_cast<DWORD*>(0x140001337);
|
||||
if (value != 0xFFB8006D && value != 0xFFB80080)
|
||||
const auto value = *reinterpret_cast<DWORD*>(0x1337_b);
|
||||
if (value != 0x60202B6A && value != 0xBC0E9FE)
|
||||
{
|
||||
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version(1.4)");
|
||||
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -90,7 +91,7 @@ namespace system_check
|
||||
if (!is_valid())
|
||||
{
|
||||
MessageBoxA(nullptr, "Your game files are outdated or unsupported.\n"
|
||||
"Please get the latest officially supported Call of Duty: Modern Warfare Remastered 1.4 files, or you will get random crashes and issues.",
|
||||
"Please get the latest officially supported Call of Duty: Modern Warfare Remastered files, or you will get random crashes and issues.",
|
||||
"Invalid game files!", MB_ICONINFORMATION);
|
||||
}
|
||||
}
|
||||
|
@ -7,164 +7,481 @@
|
||||
#include "scheduler.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
#include "ui_scripting.hpp"
|
||||
#include "localized_strings.hpp"
|
||||
#include "console.hpp"
|
||||
#include "game_module.hpp"
|
||||
#include "fps.hpp"
|
||||
#include "server_list.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "mods.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
#include "scripting.hpp"
|
||||
|
||||
#include "game/ui_scripting/lua/engine.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
#include "game/ui_scripting/lua/error.hpp"
|
||||
#include "game/scripting/execution.hpp"
|
||||
|
||||
#include "ui_scripting.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/binary_resource.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions;
|
||||
std::unordered_map<game::hks::cclosure*, std::function<arguments(const function_arguments& args)>> converted_functions;
|
||||
|
||||
utils::hook::detour hksi_lual_error_hook;
|
||||
utils::hook::detour hksi_lual_error_hook2;
|
||||
utils::hook::detour hks_start_hook;
|
||||
utils::hook::detour hks_shutdown_hook;
|
||||
utils::hook::detour hks_allocator_hook;
|
||||
utils::hook::detour hks_frame_hook;
|
||||
utils::hook::detour lui_error_hook;
|
||||
utils::hook::detour hksi_hks_error_hook;
|
||||
utils::hook::detour hks_package_require_hook;
|
||||
|
||||
bool error_hook_enabled = false;
|
||||
utils::hook::detour hks_load_hook;
|
||||
|
||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
||||
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
||||
const auto lua_json = utils::nt::load_resource(LUA_JSON);
|
||||
|
||||
struct script
|
||||
{
|
||||
char va_buffer[2048] = {0};
|
||||
std::string name;
|
||||
std::string root;
|
||||
};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(va_buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
struct globals_t
|
||||
{
|
||||
std::string in_require_script;
|
||||
std::vector<script> loaded_scripts;
|
||||
bool load_raw_script{};
|
||||
std::string raw_script_name{};
|
||||
};
|
||||
|
||||
const auto formatted = std::string(va_buffer);
|
||||
globals_t globals{};
|
||||
|
||||
if (!error_hook_enabled)
|
||||
bool is_loaded_script(const std::string& name)
|
||||
{
|
||||
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
||||
{
|
||||
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
||||
if (i->name == name)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(formatted);
|
||||
return false;
|
||||
}
|
||||
|
||||
int hksi_hks_error_stub(game::hks::lua_State* s, int a2)
|
||||
std::string get_root_script(const std::string& name)
|
||||
{
|
||||
if (!error_hook_enabled)
|
||||
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
||||
{
|
||||
return hksi_hks_error_hook.invoke<int>(s, a2);
|
||||
if (i->name == name)
|
||||
{
|
||||
return i->root;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown error");
|
||||
return {};
|
||||
}
|
||||
|
||||
void lui_error_stub(game::hks::lua_State* s)
|
||||
table get_globals()
|
||||
{
|
||||
if (!error_hook_enabled)
|
||||
const auto state = *game::hks::lua_state;
|
||||
return state->globals.v.table;
|
||||
}
|
||||
|
||||
void print_error(const std::string& error)
|
||||
{
|
||||
console::error("************** LUI script execution error **************\n");
|
||||
console::error("%s\n", error.data());
|
||||
console::error("********************************************************\n");
|
||||
}
|
||||
|
||||
void print_loading_script(const std::string& name)
|
||||
{
|
||||
console::info("Loading LUI script '%s'\n", name.data());
|
||||
}
|
||||
|
||||
std::string get_current_script()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
game::hks::lua_Debug info{};
|
||||
game::hks::hksi_lua_getstack(state, 1, &info);
|
||||
game::hks::hksi_lua_getinfo(state, "nSl", &info);
|
||||
return info.short_src;
|
||||
}
|
||||
|
||||
int load_buffer(const std::string& name, const std::string& data)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto sharing_mode = state->m_global->m_bytecodeSharingMode;
|
||||
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
|
||||
const auto _0 = gsl::finally([&]()
|
||||
{
|
||||
lui_error_hook.invoke<void>(s);
|
||||
state->m_global->m_bytecodeSharingMode = sharing_mode;
|
||||
});
|
||||
|
||||
game::hks::HksCompilerSettings compiler_settings{};
|
||||
return game::hks::hksi_hksL_loadbuffer(state, &compiler_settings, data.data(), data.size(), name.data());
|
||||
}
|
||||
|
||||
void load_script(const std::string& name, const std::string& data)
|
||||
{
|
||||
globals.loaded_scripts.push_back({name, name});
|
||||
|
||||
const auto lua = get_globals();
|
||||
const auto load_results = lua["loadstring"](data, name);
|
||||
|
||||
if (load_results[0].is<function>())
|
||||
{
|
||||
const auto results = lua["pcall"](load_results);
|
||||
if (!results[0].as<bool>())
|
||||
{
|
||||
print_error(results[1].as<std::string>());
|
||||
}
|
||||
}
|
||||
else if (load_results[1].is<std::string>())
|
||||
{
|
||||
print_error(load_results[1].as<std::string>());
|
||||
}
|
||||
}
|
||||
|
||||
void load_scripts(const std::string& script_dir)
|
||||
{
|
||||
if (!utils::io::directory_exists(script_dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto count = static_cast<int>(s->m_apistack.top - s->m_apistack.base);
|
||||
const auto arguments = get_return_values(count);
|
||||
const auto scripts = utils::io::list_files(script_dir);
|
||||
|
||||
std::string error_str = "LUI Error";
|
||||
if (count && arguments[0].is<std::string>())
|
||||
for (const auto& script : scripts)
|
||||
{
|
||||
error_str = arguments[0].as<std::string>();
|
||||
std::string data{};
|
||||
if (std::filesystem::is_directory(script) && utils::io::read_file(script + "/__init__.lua", &data))
|
||||
{
|
||||
print_loading_script(script);
|
||||
load_script(script + "/__init__.lua", data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup_functions()
|
||||
{
|
||||
const auto lua = get_globals();
|
||||
|
||||
lua["io"]["fileexists"] = utils::io::file_exists;
|
||||
lua["io"]["writefile"] = utils::io::write_file;
|
||||
lua["io"]["movefile"] = utils::io::move_file;
|
||||
lua["io"]["filesize"] = utils::io::file_size;
|
||||
lua["io"]["createdirectory"] = utils::io::create_directory;
|
||||
lua["io"]["directoryexists"] = utils::io::directory_exists;
|
||||
lua["io"]["directoryisempty"] = utils::io::directory_is_empty;
|
||||
lua["io"]["listfiles"] = utils::io::list_files;
|
||||
lua["io"]["removefile"] = utils::io::remove_file;
|
||||
lua["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
|
||||
|
||||
using game = table;
|
||||
auto game_type = game();
|
||||
lua["game"] = game_type;
|
||||
|
||||
game_type["getfps"] = [](const game&)
|
||||
{
|
||||
return fps::get_fps();
|
||||
};
|
||||
|
||||
game_type["getping"] = [](const game&)
|
||||
{
|
||||
if ((*::game::mp::client_state) == nullptr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (*::game::mp::client_state)->ping;
|
||||
};
|
||||
|
||||
game_type["issingleplayer"] = [](const game&)
|
||||
{
|
||||
return ::game::environment::is_sp();
|
||||
};
|
||||
|
||||
game_type["ismultiplayer"] = [](const game&)
|
||||
{
|
||||
return ::game::environment::is_mp();
|
||||
};
|
||||
|
||||
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
|
||||
const std::string& value)
|
||||
{
|
||||
localized_strings::override(string, value);
|
||||
};
|
||||
|
||||
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
|
||||
{
|
||||
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
|
||||
{
|
||||
table[key] = value;
|
||||
});
|
||||
};
|
||||
|
||||
game_type["sharedget"] = [](const game&, const std::string& key)
|
||||
{
|
||||
std::string result;
|
||||
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
|
||||
{
|
||||
result = table[key];
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
game_type["sharedclear"] = [](const game&)
|
||||
{
|
||||
scripting::shared_table.access([](scripting::shared_table_t& table)
|
||||
{
|
||||
table.clear();
|
||||
});
|
||||
};
|
||||
|
||||
game_type["assetlist"] = [](const game&, const std::string& type_string)
|
||||
{
|
||||
auto table_ = table();
|
||||
auto index = 1;
|
||||
auto type_index = -1;
|
||||
|
||||
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||
{
|
||||
if (type_string == ::game::g_assetNames[i])
|
||||
{
|
||||
type_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_index == -1)
|
||||
{
|
||||
throw std::runtime_error("Asset type does not exist");
|
||||
}
|
||||
|
||||
const auto type = static_cast<::game::XAssetType>(type_index);
|
||||
fastfiles::enum_assets(type, [type, &table_, &index](const ::game::XAssetHeader header)
|
||||
{
|
||||
const auto asset = ::game::XAsset{type, header};
|
||||
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
|
||||
table_[index++] = asset_name;
|
||||
}, true);
|
||||
|
||||
return table_;
|
||||
};
|
||||
|
||||
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
|
||||
{
|
||||
const auto alternate = name.starts_with("alt_");
|
||||
const auto weapon = ::game::G_GetWeaponForName(name.data());
|
||||
|
||||
char buffer[0x400] = {0};
|
||||
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
|
||||
|
||||
return std::string(buffer);
|
||||
};
|
||||
|
||||
game_type["getloadedmod"] = [](const game&)
|
||||
{
|
||||
return mods::mod_path;
|
||||
};
|
||||
|
||||
if (::game::environment::is_sp())
|
||||
{
|
||||
using player = table;
|
||||
auto player_type = player();
|
||||
lua["player"] = player_type;
|
||||
|
||||
player_type["notify"] = [](const player&, const std::string& name, const variadic_args& va)
|
||||
{
|
||||
if (!::game::CL_IsCgameInitialized() || !::game::SV_Loaded())
|
||||
{
|
||||
throw std::runtime_error("Not in game");
|
||||
}
|
||||
|
||||
const auto to_string = get_globals()["tostring"];
|
||||
const auto arguments = get_return_values();
|
||||
std::vector<std::string> args{};
|
||||
for (const auto& value : va)
|
||||
{
|
||||
const auto value_str = to_string(value);
|
||||
|
||||
args.push_back(value_str[0].as<std::string>());
|
||||
}
|
||||
|
||||
::scheduler::once([name, args]()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<scripting::script_value> arguments{};
|
||||
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
|
||||
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
||||
scripting::notify(player, name, arguments);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}, ::scheduler::pipeline::server);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
globals = {};
|
||||
|
||||
const auto lua = get_globals();
|
||||
lua["EnableGlobals"]();
|
||||
|
||||
setup_functions();
|
||||
|
||||
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(SELECT_VALUE(0x93490_b, 0x209EB0_b)));
|
||||
lua["table"]["unpack"] = lua["unpack"];
|
||||
lua["luiglobals"] = lua;
|
||||
|
||||
load_script("lui_common", lui_common);
|
||||
load_script("lua_json", lua_json);
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/");
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/sp/");
|
||||
}
|
||||
else
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/mp/");
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(error_str);
|
||||
}
|
||||
|
||||
void try_start()
|
||||
{
|
||||
try
|
||||
{
|
||||
start();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
console::error("Failed to load LUI scripts: %s\n", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void* hks_start_stub(char a1)
|
||||
{
|
||||
const auto _1 = gsl::finally([]()
|
||||
{
|
||||
ui_scripting::lua::engine::start();
|
||||
});
|
||||
|
||||
const auto _0 = gsl::finally(&try_start);
|
||||
return hks_start_hook.invoke<void*>(a1);
|
||||
}
|
||||
|
||||
void hks_shutdown_stub()
|
||||
{
|
||||
converted_functions.clear();
|
||||
ui_scripting::lua::engine::stop();
|
||||
hks_shutdown_hook.invoke<void*>();
|
||||
globals = {};
|
||||
return hks_shutdown_hook.invoke<void>();
|
||||
}
|
||||
|
||||
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
|
||||
void* hks_package_require_stub(game::hks::lua_State* state)
|
||||
{
|
||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory);
|
||||
if (converted_functions.find(closure) != converted_functions.end())
|
||||
const auto script = get_current_script();
|
||||
const auto root = get_root_script(script);
|
||||
globals.in_require_script = root;
|
||||
return hks_package_require_hook.invoke<void*>(state);
|
||||
}
|
||||
|
||||
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
|
||||
{
|
||||
if (!is_loaded_script(globals.in_require_script))
|
||||
{
|
||||
converted_functions.erase(closure);
|
||||
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
||||
}
|
||||
|
||||
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
|
||||
}
|
||||
}
|
||||
const auto folder = globals.in_require_script.substr(0, globals.in_require_script.find_last_of("/\\"));
|
||||
const std::string name_ = name;
|
||||
const std::string target_script = folder + "/" + name_ + ".lua";
|
||||
|
||||
int main_function_handler(game::hks::lua_State* state)
|
||||
{
|
||||
const auto value = state->m_apistack.base[-1];
|
||||
if (value.t != game::hks::TCFUNCTION)
|
||||
if (utils::io::file_exists(target_script))
|
||||
{
|
||||
globals.load_raw_script = true;
|
||||
globals.raw_script_name = target_script;
|
||||
return static_cast<game::XAssetHeader>(reinterpret_cast<game::LuaFile*>(1));
|
||||
}
|
||||
else if (name_.starts_with("ui/LUI/"))
|
||||
{
|
||||
return game::DB_FindXAssetHeader(type, name, allow_create_default);
|
||||
}
|
||||
|
||||
return static_cast<game::XAssetHeader>(nullptr);
|
||||
}
|
||||
|
||||
int hks_load_stub(game::hks::lua_State* state, void* compiler_options,
|
||||
void* reader, void* reader_data, const char* chunk_name)
|
||||
{
|
||||
if (globals.load_raw_script)
|
||||
{
|
||||
globals.load_raw_script = false;
|
||||
globals.loaded_scripts.push_back({globals.raw_script_name, globals.in_require_script});
|
||||
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
|
||||
}
|
||||
else
|
||||
{
|
||||
return hks_load_hook.invoke<int>(state, compiler_options, reader,
|
||||
reader_data, chunk_name);
|
||||
}
|
||||
}
|
||||
|
||||
int main_handler(game::hks::lua_State* state)
|
||||
{
|
||||
const auto value = state->m_apistack.base[-1];
|
||||
if (value.t != game::hks::TCFUNCTION)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto closure = value.v.cClosure;
|
||||
if (converted_functions.find(closure) == converted_functions.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& function = converted_functions[closure];
|
||||
|
||||
try
|
||||
{
|
||||
const auto args = get_return_values();
|
||||
const auto results = function(args);
|
||||
|
||||
for (const auto& result : results)
|
||||
{
|
||||
push_value(result);
|
||||
}
|
||||
|
||||
return static_cast<int>(results.size());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
game::hks::hksi_luaL_error(state, e.what());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(value.v.cClosure);
|
||||
if (converted_functions.find(closure) == converted_functions.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& function = converted_functions[closure];
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
const auto arguments = get_return_values(count);
|
||||
const auto s = function.lua_state();
|
||||
|
||||
std::vector<sol::lua_value> converted_args;
|
||||
|
||||
for (const auto& argument : arguments)
|
||||
{
|
||||
converted_args.push_back(lua::convert(s, argument));
|
||||
}
|
||||
|
||||
const auto results = function(sol::as_args(converted_args));
|
||||
lua::handle_error(results);
|
||||
|
||||
for (const auto& result : results)
|
||||
{
|
||||
push_value(lua::convert({s, result}));
|
||||
}
|
||||
|
||||
return results.return_count();
|
||||
}
|
||||
|
||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function)
|
||||
template <typename F>
|
||||
game::hks::cclosure* convert_function(F f)
|
||||
{
|
||||
converted_functions[closure] = function;
|
||||
}
|
||||
|
||||
void clear_converted_functions()
|
||||
{
|
||||
converted_functions.clear();
|
||||
}
|
||||
|
||||
void enable_error_hook()
|
||||
{
|
||||
error_hook_enabled = true;
|
||||
}
|
||||
|
||||
void disable_error_hook()
|
||||
{
|
||||
error_hook_enabled = false;
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto closure = game::hks::cclosure_Create(state, main_handler, 0, 0, 0);
|
||||
converted_functions[closure] = wrap_function(f);
|
||||
return closure;
|
||||
}
|
||||
|
||||
bool lui_running()
|
||||
@ -183,27 +500,22 @@ namespace ui_scripting
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui);
|
||||
utils::hook::call(SELECT_VALUE(0xE7419_b, 0x25E809_b), db_find_xasset_header_stub);
|
||||
utils::hook::call(SELECT_VALUE(0xE72CB_b, 0x25E6BB_b), db_find_xasset_header_stub);
|
||||
|
||||
hks_start_hook.create(SELECT_VALUE(0x1400E4B40, 0x140176A40), hks_start_stub);
|
||||
hks_shutdown_hook.create(SELECT_VALUE(0x1400DD3D0, 0x14016CA80), hks_shutdown_stub);
|
||||
hksi_lual_error_hook.create(SELECT_VALUE(0x1400A5EA0, 0x14012F300), hksi_lual_error_stub);
|
||||
hks_allocator_hook.create(SELECT_VALUE(0x14009B570, 0x14012BAC0), hks_allocator_stub);
|
||||
lui_error_hook.create(SELECT_VALUE(0x14007D7D0, 0x14010C9E0), lui_error_stub);
|
||||
hksi_hks_error_hook.create(SELECT_VALUE(0x14009DD80, 0x14012E390), hksi_hks_error_stub);
|
||||
hks_load_hook.create(SELECT_VALUE(0xB46F0_b, 0x22C180_b), hks_load_stub);
|
||||
|
||||
if (game::environment::is_mp())
|
||||
{
|
||||
hksi_lual_error_hook2.create(0x1401366B0, hksi_lual_error_stub);
|
||||
}
|
||||
hks_package_require_hook.create(SELECT_VALUE(0x90070_b, 0x214040_b), hks_package_require_stub);
|
||||
hks_start_hook.create(SELECT_VALUE(0x103C50_b, 0x27A790_b), hks_start_stub);
|
||||
hks_shutdown_hook.create(SELECT_VALUE(0xFB370_b, 0x2707C0_b), hks_shutdown_stub);
|
||||
|
||||
command::add("lui_restart", []()
|
||||
{
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400DD3D0, 0x14016CA80));
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400E6170, 0x1401780D0));
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0xFB370_b, 0x2707C0_b));
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0x1052C0_b, 0x27BEC0_b));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(ui_scripting::component)
|
||||
REGISTER_COMPONENT(ui_scripting::component)
|
||||
|
@ -1,14 +1,49 @@
|
||||
#pragma once
|
||||
#include "game/ui_scripting/lua/value_conversion.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
int main_function_handler(game::hks::lua_State* state);
|
||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function);
|
||||
void clear_converted_functions();
|
||||
template <class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f](const function_arguments& args)
|
||||
{
|
||||
f(args[I]...);
|
||||
return arguments{{}};
|
||||
};
|
||||
}
|
||||
|
||||
void enable_error_hook();
|
||||
void disable_error_hook();
|
||||
template <class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<arguments(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f](const function_arguments& args)
|
||||
{
|
||||
return f(args[I]...);
|
||||
};
|
||||
}
|
||||
|
||||
template <typename R, class... Args, std::size_t... I>
|
||||
auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>)
|
||||
{
|
||||
return [f](const function_arguments& args)
|
||||
{
|
||||
return arguments{f(args[I]...)};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename R, class... Args>
|
||||
auto wrap_function(const std::function<R(Args...)>& f)
|
||||
{
|
||||
return wrap_function(f, std::index_sequence_for<Args...>{});
|
||||
}
|
||||
|
||||
template <class F>
|
||||
auto wrap_function(F f)
|
||||
{
|
||||
return wrap_function(std::function(f));
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
game::hks::cclosure* convert_function(F f);
|
||||
|
||||
bool lui_running();
|
||||
}
|
||||
}
|
||||
|
@ -468,4 +468,4 @@ namespace updater
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(updater::component)
|
||||
//REGISTER_COMPONENT(updater::component)
|
||||
|
@ -52,4 +52,4 @@ namespace videos
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(videos::component)
|
||||
//REGISTER_COMPONENT(videos::component)
|
||||
|
@ -10,36 +10,40 @@ namespace virtuallobby
|
||||
{
|
||||
namespace
|
||||
{
|
||||
game::dvar_t* virtualLobby_fovscale;
|
||||
game::dvar_t* virtual_lobby_fovscale;
|
||||
|
||||
const auto get_fovscale_stub = utils::hook::assemble([](utils::hook::assembler& a)
|
||||
void* get_get_fovscale_stub()
|
||||
{
|
||||
const auto ret = a.newLabel();
|
||||
const auto original = a.newLabel();
|
||||
return utils::hook::assemble([](utils::hook::assembler& a)
|
||||
{
|
||||
const auto ret = a.newLabel();
|
||||
const auto original = a.newLabel();
|
||||
|
||||
a.pushad64();
|
||||
a.mov(rax, qword_ptr(0x1425F7210)); // virtualLobbyInFiringRange
|
||||
a.cmp(byte_ptr(rax, 0x10), 1);
|
||||
a.je(original);
|
||||
a.call_aligned(game::VirtualLobby_Loaded);
|
||||
a.cmp(al, 0);
|
||||
a.je(original);
|
||||
a.pushad64();
|
||||
a.mov(rax, qword_ptr(0x2999CE8_b)); // virtualLobbyInFiringRange
|
||||
a.cmp(byte_ptr(rax, 0x10), 1);
|
||||
a.je(original);
|
||||
a.call_aligned(game::VirtualLobby_Loaded);
|
||||
a.cmp(al, 0);
|
||||
a.je(original);
|
||||
|
||||
// virtuallobby
|
||||
a.popad64();
|
||||
a.mov(rax, ptr(reinterpret_cast<int64_t>(&virtualLobby_fovscale)));
|
||||
a.jmp(ret);
|
||||
// virtuallobby
|
||||
a.popad64();
|
||||
a.mov(rax, ptr(reinterpret_cast<int64_t>(&virtual_lobby_fovscale)));
|
||||
a.jmp(ret);
|
||||
|
||||
// original
|
||||
a.bind(original);
|
||||
a.popad64();
|
||||
a.mov(rax, qword_ptr(0x1413A8580));
|
||||
a.jmp(ret);
|
||||
// original
|
||||
a.bind(original);
|
||||
a.popad64();
|
||||
a.mov(rax, qword_ptr(0x14C4EC8_b));
|
||||
a.jmp(ret);
|
||||
|
||||
a.bind(ret);
|
||||
a.mov(rcx, 0x142935000);
|
||||
a.jmp(0x1400B556A);
|
||||
});
|
||||
a.bind(ret);
|
||||
a.mov(rdi, rax);
|
||||
a.mov(ecx, 8);
|
||||
a.jmp(0x104545_b);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -52,11 +56,10 @@ namespace virtuallobby
|
||||
return;
|
||||
}
|
||||
|
||||
virtualLobby_fovscale = dvars::register_float("virtualLobby_fovScale", 0.7f, 0.0f, 2.0f,
|
||||
virtual_lobby_fovscale = dvars::register_float_hashed("virtualLobby_fovScale", 0.7f, 0.0f, 2.0f,
|
||||
game::DVAR_FLAG_SAVED, "Field of view scaled for the virtual lobby");
|
||||
|
||||
utils::hook::nop(0x1400B555C, 14);
|
||||
utils::hook::jump(0x1400B555C, get_fovscale_stub, true);
|
||||
utils::hook::jump(0x104539_b, get_get_fovscale_stub(), true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -170,4 +170,41 @@ namespace demonware
|
||||
buffer->read_blob(&this->data);
|
||||
}
|
||||
};
|
||||
|
||||
class bdFile2 final : public bdTaskResult
|
||||
{
|
||||
public:
|
||||
uint32_t unk1;
|
||||
uint32_t unk2;
|
||||
uint32_t unk3;
|
||||
bool priv;
|
||||
uint64_t owner_id;
|
||||
std::string platform;
|
||||
std::string filename;
|
||||
std::string data;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint32(this->unk1);
|
||||
buffer->write_uint32(this->unk2);
|
||||
buffer->write_uint32(this->unk3);
|
||||
buffer->write_bool(this->priv);
|
||||
buffer->write_uint64(this->owner_id);
|
||||
buffer->write_string(this->platform);
|
||||
buffer->write_string(this->filename);
|
||||
buffer->write_blob(this->data);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint32(&this->unk1);
|
||||
buffer->read_uint32(&this->unk2);
|
||||
buffer->read_uint32(&this->unk3);
|
||||
buffer->read_bool(&this->priv);
|
||||
buffer->read_uint64(&this->owner_id);
|
||||
buffer->read_string(&this->platform);
|
||||
buffer->read_string(&this->filename);
|
||||
buffer->read_blob(&this->data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ namespace demonware
|
||||
this->register_task(12, &bdStorage::unk12);
|
||||
|
||||
this->map_publisher_resource("motd-.*\\.txt", DW_MOTD);
|
||||
this->map_publisher_resource("ffotd-.*\\.ff", DW_FASTFILE);
|
||||
// this->map_publisher_resource("ffotd-.*\\.ff", DW_FASTFILE);
|
||||
this->map_publisher_resource("playlists(_.+)?\\.aggr", DW_PLAYLISTS);
|
||||
}
|
||||
|
||||
@ -172,15 +172,16 @@ namespace demonware
|
||||
const auto path = get_user_file_path(filename);
|
||||
utils::io::write_file(path, data);
|
||||
|
||||
auto* info = new bdFileInfo;
|
||||
auto* info = new bdFile2;
|
||||
|
||||
info->file_id = *reinterpret_cast<const uint64_t*>(utils::cryptography::sha1::compute(filename).data());
|
||||
info->unk1 = 0;
|
||||
info->unk2 = 0;
|
||||
info->unk3 = 0;
|
||||
info->priv = false;
|
||||
info->owner_id = owner;
|
||||
info->platform = platform;
|
||||
info->filename = filename;
|
||||
info->create_time = uint32_t(time(nullptr));
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = uint64_t(owner);
|
||||
info->priv = priv;
|
||||
info->data = data;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: set user file: %s\n", filename.data());
|
||||
@ -221,6 +222,9 @@ namespace demonware
|
||||
const auto path = get_user_file_path(filename);
|
||||
if (!utils::io::read_file(path, &data))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: get user file: missing file: %s, %s, %s\n", game.data(), filename.data(), platform.data());
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,8 @@ namespace demonware
|
||||
{
|
||||
bdUNK80::bdUNK80() : service(80, "bdUNK80")
|
||||
{
|
||||
this->register_task(42, &bdUNK80::unk42);
|
||||
this->register_task(42, &bdUNK80::unk42); // COD POINTS purchase ?
|
||||
// this->register_task(43, &bdUNK80::unk43); COD POINTS purchase ?
|
||||
this->register_task(49, &bdUNK80::unk49);
|
||||
this->register_task(60, &bdUNK80::unk60);
|
||||
this->register_task(130, &bdUNK80::unk130);
|
||||
|
@ -72,9 +72,11 @@ namespace dvars
|
||||
switch (type)
|
||||
{
|
||||
case game::dvar_type::boolean:
|
||||
case game::dvar_type::boolean_hashed:
|
||||
return "Domain is 0 or 1"s;
|
||||
|
||||
case game::dvar_type::value:
|
||||
case game::dvar_type::value_hashed:
|
||||
if (domain.value.min == -FLT_MAX)
|
||||
{
|
||||
if (domain.value.max == FLT_MAX)
|
||||
@ -106,6 +108,7 @@ namespace dvars
|
||||
return dvar_get_vector_domain(4, domain);
|
||||
|
||||
case game::dvar_type::integer:
|
||||
case game::dvar_type::integer_hashed:
|
||||
if (domain.enumeration.stringCount == INT_MIN)
|
||||
{
|
||||
if (domain.integer.max == INT_MAX)
|
||||
@ -11836,7 +11839,7 @@ namespace dvars
|
||||
}
|
||||
|
||||
game::dvar_t* register_int(const std::string& name, int value, int min, int max,
|
||||
game::DvarFlags flags, const std::string& description)
|
||||
unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
@ -11849,7 +11852,7 @@ namespace dvars
|
||||
}
|
||||
|
||||
game::dvar_t* register_bool(const std::string& name, bool value,
|
||||
game::DvarFlags flags, const std::string& description)
|
||||
unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
@ -11862,7 +11865,7 @@ namespace dvars
|
||||
}
|
||||
|
||||
game::dvar_t* register_string(const std::string& name, const char* value,
|
||||
game::DvarFlags flags, const std::string& description)
|
||||
unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
@ -11875,7 +11878,7 @@ namespace dvars
|
||||
}
|
||||
|
||||
game::dvar_t* register_float(const std::string& name, float value, float min,
|
||||
float max, game::DvarFlags flags, const std::string& description)
|
||||
float max, unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
@ -11887,8 +11890,21 @@ namespace dvars
|
||||
return game::Dvar_RegisterFloat(hash, "", value, min, max, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* register_float_hashed(const std::string& name, float value, float min,
|
||||
float max, unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
if (can_add_dvar_to_list(name))
|
||||
{
|
||||
dvar_list.push_back({ name, description });
|
||||
}
|
||||
|
||||
return game::Dvar_RegisterFloatHashed(hash, "", value, min, max, flags);
|
||||
}
|
||||
|
||||
game::dvar_t* register_vec4(const std::string& name, float x, float y, float z,
|
||||
float w, float min, float max, game::DvarFlags flags, const std::string& description)
|
||||
float w, float min, float max, unsigned int flags, const std::string& description)
|
||||
{
|
||||
const auto hash = game::generateHashValue(name.data());
|
||||
|
||||
|
@ -50,13 +50,15 @@ namespace dvars
|
||||
std::optional<dvar_info> get_dvar_info_from_hash(const int hash);
|
||||
|
||||
game::dvar_t* register_int(const std::string& name, int value, int min, int max,
|
||||
game::DvarFlags flags, const std::string& description);
|
||||
unsigned int flags, const std::string& description);
|
||||
game::dvar_t* register_bool(const std::string& name, bool value,
|
||||
game::DvarFlags flags, const std::string& description);
|
||||
unsigned int flags, const std::string& description);
|
||||
game::dvar_t* register_string(const std::string& name, const char* value,
|
||||
game::DvarFlags flags, const std::string& description);
|
||||
unsigned int flags, const std::string& description);
|
||||
game::dvar_t* register_float(const std::string& name, float value, float min, float max,
|
||||
game::DvarFlags flags, const std::string& description);
|
||||
unsigned int flags, const std::string& description);
|
||||
game::dvar_t* register_float_hashed(const std::string& name, float value, float min, float max,
|
||||
unsigned int flags, const std::string& description);
|
||||
game::dvar_t* register_vec4(const std::string& name, float x, float y, float z, float w, float min,
|
||||
float max, game::DvarFlags flags, const std::string& description);
|
||||
float max, unsigned int flags, const std::string& description);
|
||||
}
|
||||
|
@ -3,6 +3,14 @@
|
||||
|
||||
namespace game
|
||||
{
|
||||
uint64_t base_address;
|
||||
|
||||
void load_base_address()
|
||||
{
|
||||
const auto module = GetModuleHandle(NULL);
|
||||
base_address = uint64_t(module);
|
||||
}
|
||||
|
||||
int Cmd_Argc()
|
||||
{
|
||||
return cmd_args->argc[cmd_args->nesting];
|
||||
@ -28,6 +36,62 @@ namespace game
|
||||
return !game::environment::is_sp() && *mp::virtualLobby_loaded == 1;
|
||||
}
|
||||
|
||||
void SV_GameSendServerCommand(int client_num, svscmd_type type, const char* text)
|
||||
{
|
||||
if (*mp::svs_clients == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (client_num == -1)
|
||||
{
|
||||
SV_SendServerCommand(0, type, "%s", text);
|
||||
}
|
||||
else
|
||||
{
|
||||
SV_SendServerCommand(mp::svs_clients[client_num], type, "%s", text);
|
||||
}
|
||||
}
|
||||
|
||||
void Cbuf_AddText(int local_client_num, int controller_index, const char* cmd)
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
sp::Cbuf_AddText(local_client_num, cmd);
|
||||
}
|
||||
else
|
||||
{
|
||||
mp::Cbuf_AddText(local_client_num, controller_index, cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void Cmd_TokenizeString(const char* text)
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
sp::Cmd_TokenizeString(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto a2 = 512 - *reinterpret_cast<int*>(0x3516F40_b);
|
||||
mp::Cmd_TokenizeStringWithLimit(text, a2);
|
||||
}
|
||||
}
|
||||
|
||||
void Cmd_EndTokenizeString()
|
||||
{
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return sp::Cmd_EndTokenizeString();
|
||||
}
|
||||
|
||||
const auto nesting = cmd_args->nesting;
|
||||
const auto argc = cmd_args->argc[nesting];
|
||||
--cmd_args->nesting;
|
||||
cmd_argsPrivate->totalUsedArgvPool -= argc;
|
||||
cmd_argsPrivate->totalUsedTextPool -= cmd_argsPrivate->usedTextPool[nesting];
|
||||
}
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode mode = launcher::mode::none;
|
||||
@ -102,3 +166,8 @@ namespace game
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t operator"" _b(const uintptr_t ptr)
|
||||
{
|
||||
return game::base_address + ptr;
|
||||
}
|
@ -5,10 +5,13 @@
|
||||
|
||||
#define SELECT_VALUE(sp, mp) (game::environment::is_sp() ? (sp) : (mp))
|
||||
|
||||
#define SERVER_CD_KEY "S1X-CD-Key"
|
||||
#define SERVER_CD_KEY "H1MOD-CD-Key"
|
||||
|
||||
namespace game
|
||||
{
|
||||
extern uint64_t base_address;
|
||||
void load_base_address();
|
||||
|
||||
namespace environment
|
||||
{
|
||||
launcher::mode get_mode();
|
||||
@ -37,10 +40,10 @@ namespace game
|
||||
{
|
||||
if (environment::is_sp())
|
||||
{
|
||||
return sp_object_;
|
||||
return reinterpret_cast<T*>((uint64_t)sp_object_ + base_address);
|
||||
}
|
||||
|
||||
return mp_object_;
|
||||
return reinterpret_cast<T*>((uint64_t)mp_object_ + base_address);
|
||||
}
|
||||
|
||||
operator T* () const
|
||||
@ -65,6 +68,15 @@ namespace game
|
||||
const char* SV_Cmd_Argv(int index);
|
||||
|
||||
bool VirtualLobby_Loaded();
|
||||
|
||||
void SV_GameSendServerCommand(int clientNum, svscmd_type type, const char* text);
|
||||
|
||||
void Cbuf_AddText(int local_client_num, int controller_index, const char* cmd);
|
||||
|
||||
void Cmd_TokenizeString(const char* text);
|
||||
void Cmd_EndTokenizeString();
|
||||
}
|
||||
|
||||
uintptr_t operator"" _b(const uintptr_t ptr);
|
||||
|
||||
#include "symbols.hpp"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -59,10 +59,10 @@ namespace scripting
|
||||
|
||||
script_function get_function_by_index(const unsigned index)
|
||||
{
|
||||
static const auto function_table = SELECT_VALUE(0x14B1D1B90, 0x149813EF0);
|
||||
static const auto method_table = SELECT_VALUE(0x14B1D33A0, 0x149815700);
|
||||
static const auto function_table = SELECT_VALUE(0xB8CC510_b, 0xAC83820_b);
|
||||
static const auto method_table = SELECT_VALUE(0xB8CDD60_b, 0xAC85070_b);
|
||||
|
||||
if (index < 0x301)
|
||||
if (index < 0x30A)
|
||||
{
|
||||
return reinterpret_cast<script_function*>(function_table)[index - 1];
|
||||
}
|
||||
|
@ -850,6 +850,15 @@ namespace game
|
||||
const char** argv[8];
|
||||
};
|
||||
|
||||
struct CmdArgsPrivate
|
||||
{
|
||||
char textPool[8192];
|
||||
const char* argvPool[512];
|
||||
int usedTextPool[8];
|
||||
int totalUsedArgvPool;
|
||||
int totalUsedTextPool;
|
||||
};
|
||||
|
||||
struct cmd_function_s
|
||||
{
|
||||
cmd_function_s* next;
|
||||
@ -881,11 +890,14 @@ namespace game
|
||||
enum dvar_type : std::int8_t
|
||||
{
|
||||
boolean = 0,
|
||||
boolean_hashed = 10,
|
||||
value = 1,
|
||||
value_hashed = 11,
|
||||
vec2 = 2,
|
||||
vec3 = 3,
|
||||
vec4 = 4,
|
||||
integer = 5,
|
||||
integer_hashed = 12,
|
||||
enumeration = 6,
|
||||
string = 7,
|
||||
color = 8,
|
||||
@ -1445,6 +1457,25 @@ namespace game
|
||||
{
|
||||
};
|
||||
|
||||
// made up
|
||||
struct client_state_t
|
||||
{
|
||||
char __pad0[0x4A50];
|
||||
int ping;
|
||||
char __pad1[0x8];
|
||||
int num_players;
|
||||
};
|
||||
|
||||
// made up
|
||||
struct connect_state_t
|
||||
{
|
||||
char __pad0[0xC];
|
||||
netadr_s address;
|
||||
};
|
||||
|
||||
static_assert(offsetof(client_state_t, ping) == 0x4A50);
|
||||
static_assert(offsetof(client_state_t, num_players) == 0x4A5C);
|
||||
|
||||
enum PlayerHandIndex
|
||||
{
|
||||
WEAPON_HAND_DEFAULT = 0x0,
|
||||
@ -1477,12 +1508,17 @@ namespace game
|
||||
|
||||
struct gclient_s
|
||||
{
|
||||
char __pad0[18720];
|
||||
char name[32]; // 18720
|
||||
char __pad1[752];
|
||||
int flags; // 19504
|
||||
char __pad0[2];
|
||||
char pm_type; // 2
|
||||
char __pad1[18831];
|
||||
char name[32]; // 18834
|
||||
char __pad2[622];
|
||||
int flags; // 19488
|
||||
}; // size = ?
|
||||
|
||||
static_assert(offsetof(gclient_s, name) == 18834);
|
||||
static_assert(offsetof(gclient_s, flags) == 19488);
|
||||
|
||||
struct EntityState
|
||||
{
|
||||
uint16_t entityNum;
|
||||
@ -1519,19 +1555,13 @@ namespace game
|
||||
char __pad0[265164];
|
||||
int reliableSequence;
|
||||
int reliableAcknowledge;
|
||||
char __pad1[265832];
|
||||
gentity_s* gentity; // 268976
|
||||
char name[32]; // 268984
|
||||
char __pad2[8];
|
||||
int nextSnapshotTime; // 269024
|
||||
char __pad3[544];
|
||||
LiveClientDropType liveDropRequest; //269572
|
||||
char __pad4[24];
|
||||
TestClientType testClient; // 269600
|
||||
char __pad5[347912];
|
||||
}; // size = 879616
|
||||
char __pad1[397928];
|
||||
gentity_s* gentity;
|
||||
char name[32];
|
||||
char __pad5[348752];
|
||||
}; // size = 1011960
|
||||
|
||||
static_assert(sizeof(client_t) == 879616);
|
||||
static_assert(sizeof(client_t) == 1011960);
|
||||
}
|
||||
|
||||
namespace sp
|
||||
@ -1563,6 +1593,10 @@ namespace game
|
||||
|
||||
namespace hks
|
||||
{
|
||||
struct lua_State;
|
||||
struct HashTable;
|
||||
struct cclosure;
|
||||
|
||||
struct GenericChunkHeader
|
||||
{
|
||||
unsigned __int64 m_flags;
|
||||
@ -1588,9 +1622,6 @@ namespace game
|
||||
char m_data[30];
|
||||
};
|
||||
|
||||
struct HashTable;
|
||||
struct cclosure;
|
||||
|
||||
union HksValue
|
||||
{
|
||||
cclosure* cClosure;
|
||||
@ -1602,6 +1633,8 @@ namespace game
|
||||
void* thread;
|
||||
void* ptr;
|
||||
float number;
|
||||
long long i64;
|
||||
unsigned long long ui64;
|
||||
unsigned int native;
|
||||
bool boolean;
|
||||
};
|
||||
@ -1687,6 +1720,14 @@ namespace game
|
||||
|
||||
enum HksError
|
||||
{
|
||||
HKS_NO_ERROR = 0x0,
|
||||
HKS_ERRSYNTAX = 0xFFFFFFFC,
|
||||
HKS_ERRFILE = 0xFFFFFFFB,
|
||||
HKS_ERRRUN = 0xFFFFFF9C,
|
||||
HKS_ERRMEM = 0xFFFFFF38,
|
||||
HKS_ERRERR = 0xFFFFFED4,
|
||||
HKS_THROWING_ERROR = 0xFFFFFE0C,
|
||||
HKS_GC_YIELD = 0x1,
|
||||
};
|
||||
|
||||
struct lua_Debug
|
||||
@ -1707,24 +1748,6 @@ namespace game
|
||||
int is_tail_call;
|
||||
};
|
||||
|
||||
struct lua_State : ChunkHeader
|
||||
{
|
||||
void* m_global;
|
||||
CallStack m_callStack;
|
||||
ApiStack m_apistack;
|
||||
UpValue* pending;
|
||||
HksObject globals;
|
||||
HksObject m_cEnv;
|
||||
CallSite* m_callsites;
|
||||
int m_numberOfCCalls;
|
||||
void* m_context;
|
||||
InternString* m_name;
|
||||
lua_State* m_nextState;
|
||||
lua_State* m_nextStateStack;
|
||||
Status m_status;
|
||||
HksError m_error;
|
||||
};
|
||||
|
||||
using lua_function = int(__fastcall*)(lua_State*);
|
||||
|
||||
struct luaL_Reg
|
||||
@ -1763,5 +1786,231 @@ namespace game
|
||||
InternString* m_name;
|
||||
HksObject m_upvalues[1];
|
||||
};
|
||||
|
||||
enum HksCompilerSettings_BytecodeSharingFormat
|
||||
{
|
||||
BYTECODE_DEFAULT = 0x0,
|
||||
BYTECODE_INPLACE = 0x1,
|
||||
BYTECODE_REFERENCED = 0x2,
|
||||
};
|
||||
|
||||
enum HksCompilerSettings_IntLiteralOptions
|
||||
{
|
||||
INT_LITERALS_NONE = 0x0,
|
||||
INT_LITERALS_LUD = 0x1,
|
||||
INT_LITERALS_32BIT = 0x1,
|
||||
INT_LITERALS_UI64 = 0x2,
|
||||
INT_LITERALS_64BIT = 0x2,
|
||||
INT_LITERALS_ALL = 0x3,
|
||||
};
|
||||
|
||||
struct HksCompilerSettings
|
||||
{
|
||||
int m_emitStructCode;
|
||||
const char** m_stripNames;
|
||||
int m_emitGlobalMemoization;
|
||||
int _m_isHksGlobalMemoTestingMode;
|
||||
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
|
||||
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
|
||||
int(__fastcall* m_debugMap)(const char*, int);
|
||||
};
|
||||
|
||||
enum HksBytecodeSharingMode
|
||||
{
|
||||
HKS_BYTECODE_SHARING_OFF = 0x0,
|
||||
HKS_BYTECODE_SHARING_ON = 0x1,
|
||||
HKS_BYTECODE_SHARING_SECURE = 0x2,
|
||||
};
|
||||
|
||||
struct HksGcWeights
|
||||
{
|
||||
int m_removeString;
|
||||
int m_finalizeUserdataNoMM;
|
||||
int m_finalizeUserdataGcMM;
|
||||
int m_cleanCoroutine;
|
||||
int m_removeWeak;
|
||||
int m_markObject;
|
||||
int m_traverseString;
|
||||
int m_traverseUserdata;
|
||||
int m_traverseCoroutine;
|
||||
int m_traverseWeakTable;
|
||||
int m_freeChunk;
|
||||
int m_sweepTraverse;
|
||||
};
|
||||
|
||||
struct GarbageCollector_Stack
|
||||
{
|
||||
void* m_storage;
|
||||
unsigned int m_numEntries;
|
||||
unsigned int m_numAllocated;
|
||||
};
|
||||
|
||||
struct ProtoList
|
||||
{
|
||||
void** m_protoList;
|
||||
unsigned __int16 m_protoSize;
|
||||
unsigned __int16 m_protoAllocSize;
|
||||
};
|
||||
|
||||
struct GarbageCollector
|
||||
{
|
||||
int m_target;
|
||||
int m_stepsLeft;
|
||||
int m_stepLimit;
|
||||
HksGcWeights m_costs;
|
||||
int m_unit;
|
||||
_SETJMP_FLOAT128(*m_jumpPoint)[16];
|
||||
lua_State* m_mainState;
|
||||
lua_State* m_finalizerState;
|
||||
void* m_memory;
|
||||
int m_phase;
|
||||
GarbageCollector_Stack m_resumeStack;
|
||||
GarbageCollector_Stack m_greyStack;
|
||||
GarbageCollector_Stack m_remarkStack;
|
||||
GarbageCollector_Stack m_weakStack;
|
||||
int m_finalizing;
|
||||
HksObject m_safeTableValue;
|
||||
lua_State* m_startOfStateStackList;
|
||||
lua_State* m_endOfStateStackList;
|
||||
lua_State* m_currentState;
|
||||
HksObject m_safeValue;
|
||||
void* m_compiler;
|
||||
void* m_bytecodeReader;
|
||||
void* m_bytecodeWriter;
|
||||
int m_pauseMultiplier;
|
||||
int m_stepMultiplier;
|
||||
bool m_stopped;
|
||||
int(__fastcall* m_gcPolicy)(lua_State*);
|
||||
unsigned __int64 m_pauseTriggerMemoryUsage;
|
||||
int m_stepTriggerCountdown;
|
||||
unsigned int m_stringTableIndex;
|
||||
unsigned int m_stringTableSize;
|
||||
UserData* m_lastBlackUD;
|
||||
UserData* m_activeUD;
|
||||
};
|
||||
|
||||
enum MemoryManager_ChunkColor
|
||||
{
|
||||
RED = 0x0,
|
||||
BLACK = 0x1,
|
||||
};
|
||||
|
||||
struct ChunkList
|
||||
{
|
||||
ChunkHeader m_prevToStart;
|
||||
};
|
||||
|
||||
enum Hks_DeleteCheckingMode
|
||||
{
|
||||
HKS_DELETE_CHECKING_OFF = 0x0,
|
||||
HKS_DELETE_CHECKING_ACCURATE = 0x1,
|
||||
HKS_DELETE_CHECKING_SAFE = 0x2,
|
||||
};
|
||||
|
||||
struct MemoryManager
|
||||
{
|
||||
void* (__fastcall* m_allocator)(void*, void*, unsigned __int64, unsigned __int64);
|
||||
void* m_allocatorUd;
|
||||
MemoryManager_ChunkColor m_chunkColor;
|
||||
unsigned __int64 m_used;
|
||||
unsigned __int64 m_highwatermark;
|
||||
ChunkList m_allocationList;
|
||||
ChunkList m_sweepList;
|
||||
ChunkHeader* m_lastKeptChunk;
|
||||
lua_State* m_state;
|
||||
ChunkList m_deletedList;
|
||||
int m_deleteMode;
|
||||
Hks_DeleteCheckingMode m_deleteCheckingMode;
|
||||
};
|
||||
|
||||
struct StaticStringCache
|
||||
{
|
||||
HksObject m_objects[41];
|
||||
};
|
||||
|
||||
enum HksBytecodeEndianness
|
||||
{
|
||||
HKS_BYTECODE_DEFAULT_ENDIAN = 0x0,
|
||||
HKS_BYTECODE_BIG_ENDIAN = 0x1,
|
||||
HKS_BYTECODE_LITTLE_ENDIAN = 0x2,
|
||||
};
|
||||
|
||||
struct RuntimeProfileData_Stats
|
||||
{
|
||||
unsigned __int64 hksTime;
|
||||
unsigned __int64 callbackTime;
|
||||
unsigned __int64 gcTime;
|
||||
unsigned __int64 cFinalizerTime;
|
||||
unsigned __int64 compilerTime;
|
||||
unsigned int hkssTimeSamples;
|
||||
unsigned int callbackTimeSamples;
|
||||
unsigned int gcTimeSamples;
|
||||
unsigned int compilerTimeSamples;
|
||||
unsigned int num_newuserdata;
|
||||
unsigned int num_tablerehash;
|
||||
unsigned int num_pushstring;
|
||||
unsigned int num_pushcfunction;
|
||||
unsigned int num_newtables;
|
||||
};
|
||||
|
||||
struct RuntimeProfileData
|
||||
{
|
||||
__int64 stackDepth;
|
||||
__int64 callbackDepth;
|
||||
unsigned __int64 lastTimer;
|
||||
RuntimeProfileData_Stats frameStats;
|
||||
unsigned __int64 gcStartTime;
|
||||
unsigned __int64 finalizerStartTime;
|
||||
unsigned __int64 compilerStartTime;
|
||||
unsigned __int64 compilerStartGCTime;
|
||||
unsigned __int64 compilerStartGCFinalizerTime;
|
||||
unsigned __int64 compilerCallbackStartTime;
|
||||
__int64 compilerDepth;
|
||||
void* outFile;
|
||||
lua_State* rootState;
|
||||
};
|
||||
|
||||
struct HksGlobal
|
||||
{
|
||||
MemoryManager m_memory;
|
||||
GarbageCollector m_collector;
|
||||
StringTable m_stringTable;
|
||||
HksBytecodeSharingMode m_bytecodeSharingMode;
|
||||
unsigned int m_tableVersionInitializer;
|
||||
HksObject m_registry;
|
||||
ProtoList m_protoList;
|
||||
HashTable* m_structProtoByName;
|
||||
ChunkList m_userDataList;
|
||||
lua_State* m_root;
|
||||
StaticStringCache m_staticStringCache;
|
||||
void* m_debugger;
|
||||
void* m_profiler;
|
||||
RuntimeProfileData m_runProfilerData;
|
||||
HksCompilerSettings m_compilerSettings;
|
||||
int(__fastcall* m_panicFunction)(lua_State*);
|
||||
void* m_luaplusObjectList;
|
||||
int m_heapAssertionFrequency;
|
||||
int m_heapAssertionCount;
|
||||
void (*m_logFunction)(lua_State*, const char*, ...);
|
||||
HksBytecodeEndianness m_bytecodeDumpEndianness;
|
||||
};
|
||||
|
||||
struct lua_State : ChunkHeader
|
||||
{
|
||||
HksGlobal* m_global;
|
||||
CallStack m_callStack;
|
||||
ApiStack m_apistack;
|
||||
UpValue* pending;
|
||||
HksObject globals;
|
||||
HksObject m_cEnv;
|
||||
CallSite* m_callsites;
|
||||
int m_numberOfCCalls;
|
||||
void* m_context;
|
||||
InternString* m_name;
|
||||
lua_State* m_nextState;
|
||||
lua_State* m_nextStateStack;
|
||||
Status m_status;
|
||||
HksError m_error;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -8,277 +8,300 @@ namespace game
|
||||
* Functions
|
||||
**************************************************************/
|
||||
|
||||
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x14036E600, 0x14043C580};
|
||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x1403700F0, 0x14043E090};
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0x14036E5F0, 0x14043C570};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x14036FFE0, 0x14043DF80};
|
||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x14036E960, 0x14043C8E0};
|
||||
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x14036E9C0, 0x14043C940};
|
||||
WEAK symbol<void(int type, VariableUnion u)> AddRefToValue{0x3C1F50, 0x5090E0};
|
||||
WEAK symbol<void(int type, VariableUnion u)> RemoveRefToValue{0x3C3A60, 0x50ABF0};
|
||||
WEAK symbol<void(unsigned int id)> AddRefToObject{0x3C1F40, 0x5090D0};
|
||||
WEAK symbol<void(unsigned int id)> RemoveRefToObject{0x3C3950, 0x50AAE0};
|
||||
WEAK symbol<unsigned int(unsigned int id)> AllocThread{0x3C22B0, 0x509440};
|
||||
WEAK symbol<ObjectVariableValue*(unsigned int* id)> AllocVariable{0x3C2310, 0x5094A0};
|
||||
|
||||
namespace sp
|
||||
{
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x3764A0, 0x0};
|
||||
}
|
||||
|
||||
namespace mp
|
||||
{
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cbuf_AddText{0x0, 0x1CF480};
|
||||
}
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x140342EB0, 0x1404033B0};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* buffer,
|
||||
void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x140342FC0, 0x1404034C0};
|
||||
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x1403E3300, 0x140513FF0};
|
||||
WEAK symbol<char*(int start)> ConcatArgs{0x1402697F0, 0x140335D70};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x140343980, 0x140403F60};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x1403433E0, 0x140403950};
|
||||
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x140343FF0, 0x1404045D0};
|
||||
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x140344110, 0x1404046F0};
|
||||
WEAK symbol<void()> Cmd_EndTokenizeString{0x140343630, 0x140403C20};
|
||||
void (int, int, const char*))> Cbuf_ExecuteBufferInternal{0x3765B0, 0x155BC0};
|
||||
WEAK symbol<void(const char* message)> Conbuf_AppendText{0x0, 0x0};
|
||||
WEAK symbol<char*(int start)> ConcatArgs{0x296420, 0x413050};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x376FF0, 0x156E90};
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x376A40, 0x156880};
|
||||
WEAK symbol<void(const char*)> Cmd_RemoveCommand{0x377670, 0x157690};
|
||||
|
||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0, 0x14009D0F0};
|
||||
namespace sp
|
||||
{
|
||||
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{0x377790, 0x0};
|
||||
WEAK symbol<void()> Cmd_EndTokenizeString{0x376C90, 0x0};
|
||||
}
|
||||
|
||||
namespace mp
|
||||
{
|
||||
WEAK symbol<void(const char* text_in, int limit)> Cmd_TokenizeStringWithLimit{0x0, 0x157A40};
|
||||
}
|
||||
|
||||
WEAK symbol<void(void*, void*)> AimAssist_AddToTargetList{0x0, 0xE66C0};
|
||||
|
||||
WEAK symbol<void(unsigned int weapon, bool isAlternate,
|
||||
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x140430550, 0x1401F9670};
|
||||
char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x4B19C0, 0x2E2500};
|
||||
|
||||
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1401CE8D0, 0x1400D8310};
|
||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1405039A0};
|
||||
WEAK symbol<bool()> Com_InFrontend{0x1400E4B30, 0x140176A30};
|
||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1400DB790};
|
||||
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1403509C0, 0x1400D78A0};
|
||||
WEAK symbol<void()> Com_Quit_f{0x140352BE0, 0x1400DA830};
|
||||
WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x140353B70, 0x1400DB8A0};
|
||||
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x385280, 0x0};
|
||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0x0, 0x5AEF80};
|
||||
WEAK symbol<bool()> Com_InFrontend{0xF6430, 0x76A40};
|
||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0x0, 0x17E5F0};
|
||||
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x384820, 0x159860};
|
||||
WEAK symbol<void()> Com_Quit_f{0x0, 0x1F9280};
|
||||
WEAK symbol<void(char const* finalMessage)> Com_Shutdown{0x3A6A50, 0x0};
|
||||
|
||||
WEAK symbol<void()> Quit{0x140352D90, 0x1400DA830};
|
||||
WEAK symbol<void()> Quit{0x3A5A20, 0x17CF50};
|
||||
|
||||
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x1401389A0, 0x140220CC0};
|
||||
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x140138750, 0x140220620};
|
||||
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessage{0x15B3B0, 0x316210};
|
||||
WEAK symbol<void(int localClientNum, const char* message)> CG_GameMessageBold{0x15B110, 0x3122F0};
|
||||
WEAK symbol<void(int localClientNum, /*mp::cg_s**/void* cg,
|
||||
const char* dvar, const char* value)> CG_SetClientDvarFromServer{0, 0x140236120};
|
||||
const char* dvar, const char* value)> CG_SetClientDvarFromServer{0x0, 0x0};
|
||||
WEAK symbol<char*(const unsigned int weapon,
|
||||
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x14016EC30, 0x1400B5840};
|
||||
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x192B80, 0x104B70};
|
||||
|
||||
WEAK symbol<bool()> CL_IsCgameInitialized{0x14017EE30, 0x140245650};
|
||||
WEAK symbol<void(int a1)> CL_VirtualLobbyShutdown{0, 0x140256D40};
|
||||
WEAK symbol<bool()> CL_IsCgameInitialized{0x1A3210, 0x33C640};
|
||||
WEAK symbol<void(int a1)> CL_VirtualLobbyShutdown{0x0, 0x0};
|
||||
|
||||
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x1403C72B0, 0x1404FD0A0};
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x1403C5D50, 0x1404FBB00};
|
||||
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x1403C5C20, 0x1404FB930};
|
||||
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x140354DF0, 0x14041D830};
|
||||
WEAK symbol<const char*(dvar_t* dvar, dvar_value value)> Dvar_ValueToString{0x1403C8560, 0x1404FE660};
|
||||
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0, 0x1404FCC40};
|
||||
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x41BAD0, 0x1857D0};
|
||||
WEAK symbol<dvar_t*(const char* name)> Dvar_FindVar{0x41A600, 0x183EB0};
|
||||
WEAK symbol<void(const dvar_t* dvar)> Dvar_ClearModified{0x41A4F0, 0x183D50};
|
||||
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{0x3A7D60, 0x4EA020};
|
||||
WEAK symbol<const char*(dvar_t* dvar, bool is_hashed, dvar_value value)> Dvar_ValueToString{0x41CE00, 0x187000};
|
||||
WEAK symbol<void(dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x41B5F0, 0x185390};
|
||||
WEAK symbol<void(const char*, const char*,
|
||||
DvarSetSource)> Dvar_SetFromStringByNameFromSource{0, 0x1404FD490};
|
||||
DvarSetSource)> Dvar_SetFromStringByNameFromSource{0x41BD90, 0x185BD0};
|
||||
|
||||
WEAK symbol<dvar_t*(int hash, const char* name, bool value,
|
||||
unsigned int flags)> Dvar_RegisterBool{0x1403C47E0, 0x1404FA540};
|
||||
unsigned int flags)> Dvar_RegisterBool{0x419220, 0x182340};
|
||||
WEAK symbol<dvar_t*(int hash, const char* name, int value, int min, int max,
|
||||
unsigned int flags)> Dvar_RegisterInt{0x1403C4CC0, 0x1404FAA20};
|
||||
unsigned int flags)> Dvar_RegisterInt{0x419700, 0x182A10};
|
||||
WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min,
|
||||
float max, unsigned int flags)> Dvar_RegisterFloat{0x1403C4BB0, 0x1404FA910};
|
||||
float max, unsigned int flags)> Dvar_RegisterFloat{0x4195F0, 0x1827F0};
|
||||
WEAK symbol<dvar_t*(int hash, const char* dvarName, float value, float min,
|
||||
float max, unsigned int flags)> Dvar_RegisterFloatHashed{0x0, 0x182900};
|
||||
WEAK symbol<dvar_t*(int hash, const char* dvarName, const char* value,
|
||||
unsigned int flags)> Dvar_RegisterString{0x1403C4DA0, 0x1404FAB00};
|
||||
unsigned int flags)> Dvar_RegisterString{0x4197E0, 0x182BD0};
|
||||
WEAK symbol<dvar_t*(int dvarName, const char* a2, float x, float y, float z,
|
||||
float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x1403C5220, 0x1404FAF40};
|
||||
float w, float min, float max, unsigned int flags)> Dvar_RegisterVec4{0x419C60, 0x183010};
|
||||
|
||||
WEAK symbol<long long(const char* qpath, char** buffer)> FS_ReadFile{0x1403B9020, 0x1404EE720};
|
||||
WEAK symbol<void(void* buffer)> FS_FreeFile{0x1403B9010, 0x1404EE710};
|
||||
WEAK symbol<void(const char* gameName)> FS_Startup{0x1403B85D0, 0x1404EDD30};
|
||||
WEAK symbol<void(const char* path, const char* dir)> FS_AddLocalizedGameDirectory{0x1403B6030, 0x1404EBE20};
|
||||
WEAK symbol<long long(const char* qpath, char** buffer)> FS_ReadFile{0x40E280, 0x1EC690};
|
||||
WEAK symbol<void(void* buffer)> FS_FreeFile{0x40E270, 0x59E2F0};
|
||||
WEAK symbol<void(const char* gameName)> FS_Startup{0x40D890, 0x0};
|
||||
WEAK symbol<void(const char* path, const char* dir)> FS_AddLocalizedGameDirectory{0x40B1E0, 0x1878F0};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x14036FDD0, 0x14043DD70};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x14036FA00, 0x14043D990};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x14036F880, 0x14043D810};
|
||||
WEAK symbol<void()> GScr_LoadConsts{0x1402D13E0, 0x140393810};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x14036F4B0, 0x14043D430};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x14036F3B0, 0x14043D330};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x140370190, 0x14043E130};
|
||||
WEAK symbol<unsigned int(unsigned int, unsigned int)> GetVariable{0x3C3740, 0x50A8D0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewVariable{0x3C3360, 0x50A4F0};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int unsignedValue)> GetNewArrayVariable{0x3C31E0, 0x50A370};
|
||||
WEAK symbol<unsigned int(unsigned int parentId, unsigned int name)> FindVariable{0x3C2E00, 0x509F90};
|
||||
WEAK symbol<unsigned int(int entnum, unsigned int classnum)> FindEntityId{0x3C2D00, 0x509E90};
|
||||
WEAK symbol<void(unsigned int parentId, unsigned int index)> RemoveVariableValue{0x3C3B00, 0x50AC90};
|
||||
WEAK symbol<void(VariableValue* result, unsigned int classnum,
|
||||
int entnum, int offset)> GetEntityFieldValue{0x140373780, 0x140441780};
|
||||
int entnum, int offset)> GetEntityFieldValue{0x3C71A0, 0x50E2E0};
|
||||
|
||||
WEAK symbol<int(const char* fname)> generateHashValue{0x1400FE8A0, 0x1401B1010};
|
||||
WEAK symbol<int(const char* fname)> generateHashValue{0x11FEA0, 0x183F80};
|
||||
|
||||
WEAK symbol<void()> G_Glass_Update{0x14026C570, 0x14033A640};
|
||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x140342F90};
|
||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x1402C2A90, 0x14038C300};
|
||||
WEAK symbol<void()> G_Glass_Update{0x2992E0, 0x417940};
|
||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0x0, 0x420420};
|
||||
WEAK symbol<unsigned int(const char* name)> G_GetWeaponForName{0x2F20F0, 0x461180};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon, int dualWield,
|
||||
int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x1402C2DF0, 0x14038C750};
|
||||
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x140270980, 0x14033EDD0};
|
||||
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x1402C3750, 0x14038D1B0};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x1402C3900, 0x14038D370};
|
||||
int startInAltMode, int, int, int, char, ...)> G_GivePlayerWeapon{0x2F24F0, 0x461600};
|
||||
WEAK symbol<void(playerState_s* ps, unsigned int weapon, int hadWeapon)> G_InitializeAmmo{0x29D9E0, 0x41C170};
|
||||
WEAK symbol<void(int clientNum, unsigned int weapon)> G_SelectWeapon{0x2F2EA0, 0x462560};
|
||||
WEAK symbol<int(playerState_s* ps, unsigned int weapon)> G_TakePlayerWeapon{0x2F3050, 0x462770};
|
||||
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x1403CD230, 0x140503D00};
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x4293E0, 0x5AF2E0};
|
||||
|
||||
WEAK symbol<const char* (int, int, int)> Key_KeynumToString{0x140187CC0, 0x14024FE10};
|
||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1AC410, 0x199990};
|
||||
|
||||
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0, 0x14059A700};
|
||||
WEAK symbol<unsigned int(int)> Live_SyncOnlineDataFlags{0x0, 0x1A5C10};
|
||||
|
||||
WEAK symbol<Material* (const char* material)> Material_RegisterHandle{0x1404E48B0, 0x1405EAB30};
|
||||
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x56EA20, 0x692360};
|
||||
|
||||
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x1403C11C0, 0x1404F62F0};
|
||||
WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0x140357560, 0x1404255D0};
|
||||
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0, 0x140425790};
|
||||
WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0, 0x140425870};
|
||||
WEAK symbol<void(netadr_s*, sockaddr*)> NetadrToSockadr{0x416580, 0x59E580};
|
||||
WEAK symbol<void(netsrc_t, netadr_s*, const char*)> NET_OutOfBandPrint{0x3AA550, 0x4F1EB0};
|
||||
WEAK symbol<void(netsrc_t sock, int length, const void* data, const netadr_s* to)> NET_SendLoopPacket{0x0, 0x4F2070};
|
||||
WEAK symbol<bool(const char* s, netadr_s* a)> NET_StringToAdr{0x0, 0x4F2150};
|
||||
|
||||
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
|
||||
float* color, Material* material)> R_AddCmdDrawStretchPic{0x14017E5C0, 0x1402443A0};
|
||||
WEAK symbol<Font_s* (const char* font, int size)> R_RegisterFont{0x1404D4100, 0x1405D91E0};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x1404D43B0, 0x1405D94A0};
|
||||
WEAK symbol<int(void* font)> R_GetFontHeight{0x1405EA360, 0x1405D92C0};
|
||||
WEAK symbol<void* (int a1)> R_DrawSomething{0x1404D37B0, 0x1405D8890};
|
||||
WEAK symbol<void()> R_SyncRenderThread{0x1404F8240, 0x1405FF3A0};
|
||||
WEAK symbol<void* (const char* text, int maxChars, void* font, int fontHeight, float x,
|
||||
float* color, Material* material)> R_AddCmdDrawStretchPic{0x1A29A0, 0x33B2B0};
|
||||
WEAK symbol<Font_s*(const char* font, int size)> R_RegisterFont{0x55C4E0, 0x67F630};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{0x55C7A0, 0x67F8F0};
|
||||
WEAK symbol<int(void* font)> R_GetFontHeight{0x55C5C0, 0x67F710};
|
||||
WEAK symbol<void*(int a1)> R_GetSomething{0x55BB90, 0x67ECE0};
|
||||
WEAK symbol<void()> R_SyncRenderThread{0x582F30, 0x6A78C0};
|
||||
WEAK symbol<void*(const char* text, int maxChars, void* font, int fontHeight, float x,
|
||||
float y, float xScale, float yScale, float rotation, float* color,
|
||||
int style, int cursor_pos, char cursor_char,
|
||||
void* style_unk)> H1_AddBaseDrawTextCmd{0x1404F3DC0, 0x1405FB1F0};
|
||||
void* style_unk)> H1_AddBaseDrawTextCmd{0x57EA60, 0x6A3080};
|
||||
|
||||
#define R_AddCmdDrawText(TXT, MC, F, X, Y, XS, YS, R, C, S) \
|
||||
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S,-1, 0, game::R_DrawSomething(S))
|
||||
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, -1, 0, game::R_GetSomething(S))
|
||||
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
|
||||
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_DrawSomething(S))
|
||||
H1_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, S, CP, CC, game::R_GetSomething(S))
|
||||
|
||||
WEAK symbol<char* (GfxImage* image, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipCount,
|
||||
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const D3D11_SUBRESOURCE_DATA* initData)> Image_Setup{0x1404D7D50, 0x1405DCF90};
|
||||
uint32_t imageFlags, DXGI_FORMAT imageFormat, const char* name, const D3D11_SUBRESOURCE_DATA* initData)> Image_Setup{0x560740, 0x683890};
|
||||
|
||||
WEAK symbol<unsigned int(unsigned int localId, const char* pos,
|
||||
unsigned int paramcount)> VM_Execute{0x140376360, 0x140444350};
|
||||
unsigned int paramcount)> VM_Execute{0x3C9E50, 0x510EB0};
|
||||
|
||||
WEAK symbol<void(unsigned int id, scr_string_t stringValue,
|
||||
unsigned int paramcount)> Scr_NotifyId{0x140375800, 0x1404437E0};
|
||||
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x140370930, 0x14043E7D0};
|
||||
WEAK symbol<float(int index)> Scr_GetFloat{0x140374D20, 0x140442D10};
|
||||
WEAK symbol<const char*(int index)> Scr_GetString{0x140375210, 0x140443150};
|
||||
WEAK symbol<int()> Scr_GetNumParam{0x140374F30, 0x140442E70};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x140374460, 0x140442510};
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x140372D50, 0x140440D80};
|
||||
WEAK symbol<unsigned int(int classnum, unsigned int entnum)> Scr_GetEntityId{0x140372CA0, 0x140440CD0};
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x1402B9F60, 0x140385330};
|
||||
unsigned int paramcount)> Scr_NotifyId{0x3C92E0, 0x510340};
|
||||
WEAK symbol<const float*(const float* v)> Scr_AllocVector{0x3C42D0, 0x50B330};
|
||||
WEAK symbol<float(int index)> Scr_GetFloat{0x3C87D0, 0x50F870};
|
||||
WEAK symbol<const char*(int index)> Scr_GetString{0x3C8CC0, 0x50FCB0};
|
||||
WEAK symbol<int()> Scr_GetNumParam{0x0, 0x50F9D0};
|
||||
WEAK symbol<void()> Scr_ClearOutParams{0x3C7EF0, 0x50F070};
|
||||
WEAK symbol<scr_entref_t(unsigned int entId)> Scr_GetEntityIdRef{0x3C6760, 0x50D8E0};
|
||||
WEAK symbol<unsigned int(int classnum, unsigned int entnum)> Scr_GetEntityId{0x3C66B0, 0x50D830};
|
||||
WEAK symbol<int(unsigned int classnum, int entnum, int offset)> Scr_SetObjectField{0x2E8FC0, 0x459CD0};
|
||||
|
||||
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{0x1401981F0, 0x140288550};
|
||||
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1BCED0, 0x362840};
|
||||
|
||||
WEAK symbol<void(XAssetType type, void(__cdecl* func)(XAssetHeader, void*), const void* inData, bool includeOverride)>
|
||||
DB_EnumXAssets_Internal{0x1401C9C10, 0x1402BA830};
|
||||
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x14019A390, 0x14028BE50};
|
||||
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x14019A3B0, 0x14028BE70};
|
||||
DB_EnumXAssets_Internal{0x1F0BF0, 0x394C60};
|
||||
WEAK symbol<const char*(const XAsset* asset)> DB_GetXAssetName{0x1BF890, 0x366140};
|
||||
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x0, 0x0};
|
||||
WEAK symbol<XAssetHeader(XAssetType type, const char* name,
|
||||
int createDefault)> DB_FindXAssetHeader{0x1401CA150, 0x1402BAC70};
|
||||
int createDefault)> DB_FindXAssetHeader{0x1F1120, 0x3950C0};
|
||||
|
||||
WEAK symbol<void(int clientNum, const char* menu,
|
||||
int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x14039D5F0, 0x1404CD210};
|
||||
WEAK symbol<bool(int clientNum, const char* name, hks::lua_State* s)> LUI_BeginEvent{0x1400D27F0, 0x140161A00};
|
||||
WEAK symbol<void(hks::lua_State* s)> LUI_EndEvent{0x1400D3A80, 0x140162CD0};
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0x1400D3B70, 0x140162DC0};
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0x1400D8DB0, 0x140168150};
|
||||
int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x3F20A0, 0x1E1210};
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0xF19A0, 0x2669B0};
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0xF6C40, 0x26BDC0};
|
||||
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x1404709C0, 0x1404C7320};
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x4F43C0, 0x389F70};
|
||||
|
||||
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x14036D700, 0x14043B470};
|
||||
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x14036D9A0, 0x14043B840};
|
||||
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x14036D420, 0x14043B170};
|
||||
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x14036A310, 0x140437EA0};
|
||||
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x3C0F50, 0x507FD0};
|
||||
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x3C1210, 0x5083A0};
|
||||
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x3C0C50, 0x507CD0};
|
||||
WEAK symbol<unsigned int(const char* str)> SL_GetCanonicalString{0x3BDA20, 0x504A00};
|
||||
|
||||
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0, 0x140480860};
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x1403446C0, 0x140404CA0};
|
||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x140344740, 0x140404D20};
|
||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x140344700, 0x140404CE0};
|
||||
WEAK symbol<void(netadr_s* from)> SV_DirectConnect{0x0, 0x54DBF0};
|
||||
WEAK symbol<void(int arg, char* buffer, int bufferLength)> SV_Cmd_ArgvBuffer{0x377D40, 0x1CAC60};
|
||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x377DC0, 0x1CACE0};
|
||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x377D80, 0x1CACA0};
|
||||
|
||||
WEAK symbol<mp::gentity_s*(const char* name)> SV_AddBot{0, 0x140480190};
|
||||
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0, 0x14046E6C0};
|
||||
WEAK symbol<const char* ()> SV_BotGetRandomName{0, 0x14046DBA0};
|
||||
WEAK symbol<int(mp::gentity_s* ent)> SV_SpawnTestClient{0, 0x1404832A0};
|
||||
WEAK symbol<mp::gentity_s*(const char* name)> SV_AddBot{0x0, 0x54D4F0};
|
||||
WEAK symbol<bool(int clientNum)> SV_BotIsBot{0x0, 0x53B6D0};
|
||||
WEAK symbol<const char*()> SV_BotGetRandomName{0x0, 0x53ABD0};
|
||||
WEAK symbol<int(mp::gentity_s* ent)> SV_SpawnTestClient{0x0, 0x550580};
|
||||
|
||||
WEAK symbol<const char* (int clientNum)> SV_GetGuid{0, 0x140484B90};
|
||||
WEAK symbol<int(int clientNum)> SV_GetClientPing{0, 0x140484B70};
|
||||
WEAK symbol<playerState_s* (int num)> SV_GetPlayerstateForClientNum{0x1404426D0, 0x140484C10};
|
||||
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0, 0x140486720};
|
||||
WEAK symbol<bool()> SV_Loaded{0x140442F60, 0x1404864A0};
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0, 0x14047ED00};
|
||||
WEAK symbol<bool(const char* map)> SV_MapExists{0, 0x14047ED60};
|
||||
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0, 0x140481870};
|
||||
WEAK symbol<void(int localClientNum)> SV_FastRestart{0, 0x14047E990};
|
||||
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{
|
||||
0x1403F3A70, 0x140484AD0
|
||||
};
|
||||
WEAK symbol<const char*(int clientNum)> SV_GetGuid{0x0, 0x551D90};
|
||||
WEAK symbol<int(int clientNum)> SV_GetClientPing{0x0, 0x551D70};
|
||||
WEAK symbol<playerState_s* (int num)> SV_GetPlayerstateForClientNum{0x4C3F10, 0x551E10};
|
||||
WEAK symbol<void(int index, const char* string)> SV_SetConfigstring{0x0, 0x553E60};
|
||||
WEAK symbol<bool()> SV_Loaded{0x4C4810, 0x553970};
|
||||
WEAK symbol<void(int clientNum, const char* reason)> SV_KickClientNum{0x0, 0x54C060};
|
||||
WEAK symbol<bool(const char* map)> SV_MapExists{0x0, 0x54C0C0};
|
||||
WEAK symbol<void(mp::client_t*, const char*, int)> SV_ExecuteClientCommand{0x0, 0x0};
|
||||
WEAK symbol<void(int localClientNum)> SV_FastRestart{0x0, 0x54BE00};
|
||||
WEAK symbol<void(void* cl, int type, const char* fmt, ...)> SV_SendServerCommand{0x0, 0x1CC040};
|
||||
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x1403E3B90, 0x140514910};
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x1403E0C40, 0x140511520};
|
||||
WEAK symbol<void()> Sys_ShowConsole{0x0, 0x0};
|
||||
WEAK symbol<void(const char* error, ...)> Sys_Error{0x0, 0x1D8710};
|
||||
WEAK symbol<void(char* path, int pathSize, Sys_Folder folder, const char* filename, const char* ext)>
|
||||
Sys_BuildAbsPath{0x1403CFF90, 0x140507010};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x1403E2B10, 0x140513710};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1403580B0, 0x14042B090};
|
||||
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x1403E2820, 0x1405133B0};
|
||||
WEAK symbol<bool(const char* path)> Sys_FileExists{0x1403E0CE0, 0x1405115E0};
|
||||
Sys_BuildAbsPath{0x42C330, 0x0};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x462B30, 0x5BDDF0};
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x3AB100, 0x4F79C0};
|
||||
WEAK symbol<bool(int, void const*, const netadr_s*)> Sys_SendPacket{0x0, 0x5BDA90};
|
||||
WEAK symbol<bool(const char* path)> Sys_FileExists{0x0, 0x0};
|
||||
|
||||
WEAK symbol<const char* (const char*)> UI_GetMapDisplayName{0, 0x140408CC0};
|
||||
WEAK symbol<const char* (const char*)> UI_GetGameTypeDisplayName{0, 0x1404086A0};
|
||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x14039EFF0, 0x1404CFE60};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x1403A0F20, 0x1404D21A0};
|
||||
WEAK symbol<const char*(const char*)> UI_GetMapDisplayName{0x0, 0x4DDEE0};
|
||||
WEAK symbol<const char*(const char*)> UI_GetGameTypeDisplayName{0x0, 0x4DD8C0};
|
||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x3F3AA0, 0x1E35B0};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x3F5D90, 0x0};
|
||||
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x140350430, 0x14041C580};
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x3840A0, 0x4E8BC0};
|
||||
|
||||
WEAK symbol<void* (jmp_buf* Buf, int Value)> longjmp{0x140648FD4, 0x140779F64};
|
||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x1406BFD30, 0x1407F5F90};
|
||||
WEAK symbol<void*(jmp_buf* Buf, int Value)> longjmp{0x6DCA90, 0x826710};
|
||||
WEAK symbol<int(jmp_buf* Buf)> _setjmp{0x758980, 0x8A3190};
|
||||
|
||||
/***************************************************************
|
||||
* Variables
|
||||
**************************************************************/
|
||||
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x14AD99A10, 0x14946BA20};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0xB48FF90, 0x2ED1EB0};
|
||||
|
||||
WEAK symbol<int> g_script_error_level{0x14BCFA4A4, 0x14A33C824};
|
||||
WEAK symbol<jmp_buf> g_script_error{0x14BCFA5C0, 0x14A33C940};
|
||||
WEAK symbol<int> g_script_error_level{0xC3FD358, 0xB7AC1A4};
|
||||
WEAK symbol<jmp_buf> g_script_error{0xC3FD470, 0xB7AC2C0};
|
||||
|
||||
WEAK symbol<unsigned int> levelEntityId{0x14B65E3B0, 0x149CA0730};
|
||||
WEAK symbol<unsigned int> gameEntityId{0x14B65E3B4, 0x149CA0734};
|
||||
WEAK symbol<unsigned int> levelEntityId{0xBD58DA0, 0xB1100B0};
|
||||
WEAK symbol<unsigned int> gameEntityId{0xBD58DA4, 0xB1100B4};
|
||||
|
||||
WEAK symbol<const char*> command_whitelist{0x141079A60, 0x14120C360};
|
||||
WEAK symbol<cmd_function_s*> cmd_functions{0x14AD99AB8, 0x14946BAC8};
|
||||
WEAK symbol<CmdArgs> cmd_args{0x14AD99960, 0x14946B970};
|
||||
WEAK symbol<const char*> command_whitelist{0x115ADF0, 0x10ACB70};
|
||||
WEAK symbol<cmd_function_s*> cmd_functions{0xB490038, 0x344DF18};
|
||||
WEAK symbol<CmdArgs> cmd_args{0xB48FEE0, 0x2ED1E00};
|
||||
WEAK symbol<CmdArgsPrivate> cmd_argsPrivate{0, 0x3513F20};
|
||||
|
||||
WEAK symbol<int> g_poolSize{0x140DE84A0, 0x140FEADF0};
|
||||
WEAK symbol<scr_classStruct_t> g_classMap{0x14107AAF0, 0x1412106B0};
|
||||
WEAK symbol<int> connectionState{0x0, 0x2EC82C8};
|
||||
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0x14B686480, 0x149CC8800};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0x14BCFC6C0, 0x14A33EA40};
|
||||
WEAK symbol<function_stack_t> scr_function_stack{0x14BD06C40, 0x14A348FC0};
|
||||
WEAK symbol<int> g_poolSize{0x0, 0x0};
|
||||
|
||||
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0x14F05CE50, 0x14FD21180};
|
||||
WEAK symbol<scrVarGlob_t> scr_VarGlob{0xBD80E00, 0xB138180};
|
||||
WEAK symbol<scrVmPub_t> scr_VmPub{0xC3F4E20, 0xB7AE3C0};
|
||||
WEAK symbol<function_stack_t> scr_function_stack{0xC4015C0, 0xB7B8940};
|
||||
|
||||
WEAK symbol<int> dvarCount{0x14C217D10, 0x14D064CF4};
|
||||
WEAK symbol<dvar_t> dvarPool{0x14C217D20, 0x14D064D00};
|
||||
WEAK symbol<GfxDrawMethod_s> gfxDrawMethod{0xF7530B0, 0xE9213F0};
|
||||
|
||||
WEAK symbol<void*> DB_XAssetPool{0x140DE8C80, 0x140FEB5D0};
|
||||
WEAK symbol<const char*> g_assetNames{0x1408B0870, 0x140FEA240};
|
||||
WEAK symbol<int> dvarCount{0xC90E550, 0x2999C34};
|
||||
WEAK symbol<dvar_t> dvarPool{0xC90E560, 0x344DF20};
|
||||
|
||||
WEAK symbol<int> keyCatchers{0x14243DAF0, 0x142D0BA9C};
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0x1422A873C, 0x142C19AFC};
|
||||
WEAK symbol<void*> DB_XAssetPool{0xEC9FB0, 0x10B4460};
|
||||
WEAK symbol<const char*> g_assetNames{0x991BA0, 0x10B30D0};
|
||||
|
||||
WEAK symbol<SOCKET> query_socket{0, 0x14DDFBF98};
|
||||
WEAK symbol<int> keyCatchers{0x252AF70, 0x2EC82C4};
|
||||
WEAK symbol<PlayerKeyState> playerKeys{0x2395B0C, 0x2999E1C};
|
||||
|
||||
WEAK symbol<DWORD> threadIds{0x14B19B880, 0x149810E00};
|
||||
WEAK symbol<SOCKET> query_socket{0xD64D3F8, 0xC9DCD38};
|
||||
|
||||
WEAK symbol<DWORD> threadIds{0xB896210, 0xAC80740};
|
||||
|
||||
namespace mp
|
||||
{
|
||||
WEAK symbol<gentity_s> g_entities{0, 0x14621E530};
|
||||
WEAK symbol<client_t> svs_clients{0, 0x14B204A10};
|
||||
WEAK symbol<int> svs_numclients{0, 0x14B204A0C};
|
||||
WEAK symbol<int> gameTime{0, 0x14621BDBC};
|
||||
WEAK symbol<gentity_s> g_entities{0x0, 0x71F19E0};
|
||||
WEAK symbol<client_t*> svs_clients{0x0, 0x2DC3390};
|
||||
WEAK symbol<int> svs_numclients{0x0, 0x2DC338C};
|
||||
WEAK symbol<int> gameTime{0x0, 0x0};
|
||||
|
||||
WEAK symbol<int> ping{0, 0x142D106F0};
|
||||
WEAK symbol<int> sv_serverId_value{0x0, 0x0};
|
||||
|
||||
WEAK symbol<int> sv_serverId_value{0, 0x14A3E99B8};
|
||||
WEAK symbol<bool> virtualLobby_loaded{0x0, 0x2E6EC9D};
|
||||
|
||||
WEAK symbol<bool> virtualLobby_loaded{0, 0x142D077FD};
|
||||
WEAK symbol<client_state_t*> client_state{0x0, 0x2EC84F0};
|
||||
WEAK symbol<connect_state_t*> connect_state{0x0, 0x2EC8510};
|
||||
}
|
||||
|
||||
namespace sp
|
||||
{
|
||||
WEAK symbol<gentity_s> g_entities{0x14550DD90, 0};
|
||||
WEAK symbol<gentity_s> g_entities{0x56E74D0, 0x0};
|
||||
}
|
||||
|
||||
namespace hks
|
||||
{
|
||||
WEAK symbol<lua_State*> lua_state{0x141E2C2F8, 0x1426D3D08};
|
||||
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x14004DA90, 0x1400624F0};
|
||||
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x14009C0A0, 0x14012C600};
|
||||
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x14009D240, 0x14012D820};
|
||||
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x14009C580, 0x14012CAE0};
|
||||
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x1400C87A0, 0x140159EB0};
|
||||
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x14008B3B0, 0x14011B320};
|
||||
WEAK symbol<lua_State*> lua_state{0x1F0E408, 0x36647F8};
|
||||
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x62E60, 0x79800};
|
||||
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0xB5100, 0x22CB90};
|
||||
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0xB6320, 0x22DDB0};
|
||||
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0xB55E0, 0x22D070};
|
||||
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0xE5E40, 0x25D2F0};
|
||||
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0xA3570, 0x21B030};
|
||||
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues,
|
||||
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x14008B5D0, 0x14011B540};
|
||||
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x1400A64D0, 0x140136D30};
|
||||
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x14009EF10, 0x14012F610};
|
||||
WEAK symbol<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x14008EA00, 0x14011E970};
|
||||
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0xA3790, 0x21B250};
|
||||
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0xB7F90, 0x236CC0};
|
||||
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0xB8130, 0x22FC40};
|
||||
WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff,
|
||||
unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{0xB6B90, 0x22E620};
|
||||
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0xB84D0, 0x22FFE0};
|
||||
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0xB87A0, 0x2302B0};
|
||||
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0xBF120, 0x22F930};
|
||||
WEAK symbol<const char*> typenames{0x98CD20, 0x10AD750};
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,54 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
#include "component/ui_scripting.hpp"
|
||||
#include "component/console.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
script_value get_field(void* ptr, game::hks::HksObjectType type, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(key);
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = type;
|
||||
obj.v.ptr = ptr;
|
||||
|
||||
game::hks::hks_obj_gettable(&value, state, &obj, &state->m_apistack.top[-1]);
|
||||
state->m_apistack.top = top;
|
||||
return value;
|
||||
}
|
||||
|
||||
void set_field(void* ptr, game::hks::HksObjectType type, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = type;
|
||||
obj.v.ptr = ptr;
|
||||
|
||||
game::hks::hks_obj_settable(state, &obj, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
}
|
||||
|
||||
void push_value(const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto value_ = value.get_raw();
|
||||
*state->m_apistack.top = value_;
|
||||
*state->m_apistack.top = value.get_raw();
|
||||
state->m_apistack.top++;
|
||||
}
|
||||
|
||||
void push_value(const game::hks::HksObject& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
*state->m_apistack.top = value;
|
||||
state->m_apistack.top++;
|
||||
}
|
||||
|
||||
@ -20,13 +58,36 @@ namespace ui_scripting
|
||||
return state->m_apistack.top[-1 - offset];
|
||||
}
|
||||
|
||||
arguments get_return_values(int count)
|
||||
arguments get_return_values()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
arguments values;
|
||||
|
||||
for (auto i = count - 1; i >= 0; i--)
|
||||
{
|
||||
values.push_back(get_return_value(i));
|
||||
const auto v = get_return_value(i);
|
||||
values.push_back(v);
|
||||
}
|
||||
|
||||
if (values.size() == 0)
|
||||
{
|
||||
values.push_back({});
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
arguments get_return_values(game::hks::HksObject* base)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto count = static_cast<int>(state->m_apistack.top - base);
|
||||
arguments values;
|
||||
|
||||
for (auto i = count - 1; i >= 0; i--)
|
||||
{
|
||||
const auto v = get_return_value(i);
|
||||
values.push_back(v);
|
||||
}
|
||||
|
||||
if (values.size() == 0)
|
||||
@ -40,7 +101,7 @@ namespace ui_scripting
|
||||
bool notify(const std::string& name, const event_arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (!state)
|
||||
if (state == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -52,160 +113,63 @@ namespace ui_scripting
|
||||
{
|
||||
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
|
||||
const auto engine = globals.get("Engine").as<table>();
|
||||
const auto root = engine.get("GetLuiRoot").as<function>().call({})[0].as<userdata>();
|
||||
const auto process_event = root.get("processEvent").as<function>();
|
||||
const auto root = engine.get("GetLuiRoot")()[0].as<userdata>();
|
||||
const auto process_event = root.get("processEvent");
|
||||
|
||||
table event{};
|
||||
event.set("name", name);
|
||||
event.set("dispatchChildren", true);
|
||||
|
||||
for (const auto& arg : arguments)
|
||||
{
|
||||
event.set(arg.first, arg.second);
|
||||
}
|
||||
|
||||
process_event.call({root, event});
|
||||
process_event(root, event);
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
printf("Error processing event '%s' %s\n", name.data(), e.what());
|
||||
return false;
|
||||
console::error("Error processing event '%s' %s\n", name.data(), e.what());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
stack stack;
|
||||
push_value(function);
|
||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
const auto num_args = static_cast<int>(arguments.size());
|
||||
stack.save(num_args + 1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
return get_return_values(count);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stack.fix();
|
||||
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
|
||||
}
|
||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
||||
const auto args = get_return_values(top);
|
||||
state->m_apistack.top = top;
|
||||
return args;
|
||||
}
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
stack stack;
|
||||
push_value(key);
|
||||
stack.save(1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TUSERDATA;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||
return value;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error getting userdata field: "s + e.what());
|
||||
}
|
||||
return get_field(self.ptr, game::hks::TUSERDATA, key);
|
||||
}
|
||||
|
||||
script_value get_field(const table& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
stack stack;
|
||||
push_value(key);
|
||||
stack.save(1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TTABLE;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||
return value;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error getting table field: "s + e.what());
|
||||
}
|
||||
return get_field(self.ptr, game::hks::TTABLE, key);
|
||||
}
|
||||
|
||||
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
stack stack;
|
||||
stack.save(0);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TUSERDATA;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error setting userdata field: "s + e.what());
|
||||
}
|
||||
set_field(self.ptr, game::hks::TUSERDATA, key, value);
|
||||
}
|
||||
|
||||
void set_field(const table& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
|
||||
stack stack;
|
||||
stack.save(0);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TTABLE;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error setting table field: "s + e.what());
|
||||
}
|
||||
set_field(self.ptr, game::hks::TTABLE, key, value);
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,11 @@
|
||||
namespace ui_scripting
|
||||
{
|
||||
void push_value(const script_value& value);
|
||||
void push_value(const game::hks::HksObject& value);
|
||||
|
||||
script_value get_return_value(int offset);
|
||||
arguments get_return_values(int count);
|
||||
arguments get_return_values();
|
||||
arguments get_return_values(game::hks::HksObject* base);
|
||||
|
||||
bool notify(const std::string& name, const event_arguments& arguments);
|
||||
|
||||
|
@ -1,428 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context.hpp"
|
||||
#include "error.hpp"
|
||||
#include "value_conversion.hpp"
|
||||
#include "../../scripting/execution.hpp"
|
||||
#include "../script_value.hpp"
|
||||
#include "../execution.hpp"
|
||||
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
#include "../../../component/command.hpp"
|
||||
#include "../../../component/updater.hpp"
|
||||
#include "../../../component/fps.hpp"
|
||||
#include "../../../component/localized_strings.hpp"
|
||||
#include "../../../component/fastfiles.hpp"
|
||||
#include "../../../component/scripting.hpp"
|
||||
#include "../../../component/mods.hpp"
|
||||
#include "../../../component/discord.hpp"
|
||||
|
||||
#include "component/game_console.hpp"
|
||||
#include "component/scheduler.hpp"
|
||||
|
||||
#include <discord_rpc.h>
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const auto json_script = utils::nt::load_resource(LUA_JSON);
|
||||
|
||||
void setup_json(sol::state& state)
|
||||
{
|
||||
const auto json = state.safe_script(json_script, &sol::script_pass_on_error);
|
||||
handle_error(json);
|
||||
state["json"] = json;
|
||||
}
|
||||
|
||||
void setup_io(sol::state& state)
|
||||
{
|
||||
state["io"]["fileexists"] = utils::io::file_exists;
|
||||
state["io"]["writefile"] = utils::io::write_file;
|
||||
state["io"]["movefile"] = utils::io::move_file;
|
||||
state["io"]["filesize"] = utils::io::file_size;
|
||||
state["io"]["createdirectory"] = utils::io::create_directory;
|
||||
state["io"]["directoryexists"] = utils::io::directory_exists;
|
||||
state["io"]["directoryisempty"] = utils::io::directory_is_empty;
|
||||
state["io"]["listfiles"] = utils::io::list_files;
|
||||
state["io"]["copyfolder"] = utils::io::copy_folder;
|
||||
state["io"]["removefile"] = utils::io::remove_file;
|
||||
state["io"]["removedirectory"] = utils::io::remove_directory;
|
||||
state["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
|
||||
}
|
||||
|
||||
void setup_types(sol::state& state, scheduler& scheduler)
|
||||
{
|
||||
struct game
|
||||
{
|
||||
};
|
||||
auto game_type = state.new_usertype<game>("game_");
|
||||
state["game"] = game();
|
||||
|
||||
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||
const long long milliseconds)
|
||||
{
|
||||
return scheduler.add(callback, milliseconds, true);
|
||||
};
|
||||
|
||||
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||
const long long milliseconds)
|
||||
{
|
||||
return scheduler.add(callback, milliseconds, false);
|
||||
};
|
||||
|
||||
game_type["getfps"] = [](const game&)
|
||||
{
|
||||
return fps::get_fps();
|
||||
};
|
||||
|
||||
game_type["getping"] = [](const game&)
|
||||
{
|
||||
return *::game::mp::ping;
|
||||
};
|
||||
|
||||
game_type["issingleplayer"] = [](const game&)
|
||||
{
|
||||
return ::game::environment::is_sp();
|
||||
};
|
||||
|
||||
game_type["ismultiplayer"] = [](const game&)
|
||||
{
|
||||
return ::game::environment::is_mp();
|
||||
};
|
||||
|
||||
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
|
||||
const std::string& value)
|
||||
{
|
||||
localized_strings::override(string, value);
|
||||
};
|
||||
|
||||
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
|
||||
{
|
||||
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
|
||||
{
|
||||
table[key] = value;
|
||||
});
|
||||
};
|
||||
|
||||
game_type["sharedget"] = [](const game&, const std::string& key)
|
||||
{
|
||||
std::string result;
|
||||
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
|
||||
{
|
||||
result = table[key];
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
game_type["sharedclear"] = [](const game&)
|
||||
{
|
||||
scripting::shared_table.access([](scripting::shared_table_t& table)
|
||||
{
|
||||
table.clear();
|
||||
});
|
||||
};
|
||||
|
||||
game_type["assetlist"] = [](const game&, const sol::this_state s, const std::string& type_string)
|
||||
{
|
||||
auto table = sol::table::create(s.lua_state());
|
||||
auto index = 1;
|
||||
auto type_index = -1;
|
||||
|
||||
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||
{
|
||||
if (type_string == ::game::g_assetNames[i])
|
||||
{
|
||||
type_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_index == -1)
|
||||
{
|
||||
throw std::runtime_error("Asset type does not exist");
|
||||
}
|
||||
|
||||
const auto type = static_cast<::game::XAssetType>(type_index);
|
||||
fastfiles::enum_assets(type, [type, &table, &index](const ::game::XAssetHeader header)
|
||||
{
|
||||
const auto asset = ::game::XAsset{type, header};
|
||||
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
|
||||
table[index++] = asset_name;
|
||||
}, true);
|
||||
|
||||
return table;
|
||||
};
|
||||
|
||||
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
|
||||
{
|
||||
const auto alternate = name.starts_with("alt_");
|
||||
const auto weapon = ::game::G_GetWeaponForName(name.data());
|
||||
|
||||
char buffer[0x400] = {0};
|
||||
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
|
||||
|
||||
return std::string(buffer);
|
||||
};
|
||||
|
||||
game_type["getloadedmod"] = [](const game&)
|
||||
{
|
||||
return mods::mod_path;
|
||||
};
|
||||
|
||||
auto userdata_type = state.new_usertype<userdata>("userdata_");
|
||||
|
||||
userdata_type["new"] = sol::property(
|
||||
[](const userdata& userdata, const sol::this_state s)
|
||||
{
|
||||
return convert(s, userdata.get("new"));
|
||||
},
|
||||
[](const userdata& userdata, const sol::this_state s, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set("new", convert({s, value}));
|
||||
}
|
||||
);
|
||||
|
||||
userdata_type["get"] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, userdata.get(convert({s, key})));
|
||||
};
|
||||
|
||||
userdata_type["set"] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, userdata.get(convert({s, key})));
|
||||
};
|
||||
|
||||
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto table_type = state.new_usertype<table>("table_");
|
||||
|
||||
table_type["new"] = sol::property(
|
||||
[](const table& table, const sol::this_state s)
|
||||
{
|
||||
return convert(s, table.get("new"));
|
||||
},
|
||||
[](const table& table, const sol::this_state s, const sol::lua_value& value)
|
||||
{
|
||||
table.set("new", convert({s, value}));
|
||||
}
|
||||
);
|
||||
|
||||
table_type["get"] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type["set"] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto function_type = state.new_usertype<function>("function_");
|
||||
|
||||
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
|
||||
{
|
||||
arguments arguments{};
|
||||
|
||||
for (auto arg : va)
|
||||
{
|
||||
arguments.push_back(convert({s, arg}));
|
||||
}
|
||||
|
||||
const auto values = function.call(arguments);
|
||||
std::vector<sol::lua_value> returns;
|
||||
|
||||
for (const auto& value : values)
|
||||
{
|
||||
returns.push_back(convert(s, value));
|
||||
}
|
||||
|
||||
return sol::as_returns(returns);
|
||||
};
|
||||
|
||||
if (::game::environment::is_sp())
|
||||
{
|
||||
struct player
|
||||
{
|
||||
};
|
||||
auto player_type = state.new_usertype<player>("player_");
|
||||
state["player"] = player();
|
||||
|
||||
player_type["notify"] = [](const player&, const sol::this_state s, const std::string& name, sol::variadic_args va)
|
||||
{
|
||||
if (!::game::CL_IsCgameInitialized() || !::game::sp::g_entities[0].client)
|
||||
{
|
||||
throw std::runtime_error("Not in game");
|
||||
}
|
||||
|
||||
const sol::state_view view{s};
|
||||
const auto to_string = view["tostring"].get<sol::protected_function>();
|
||||
|
||||
std::vector<std::string> args{};
|
||||
for (auto arg : va)
|
||||
{
|
||||
args.push_back(to_string.call(arg).get<std::string>());
|
||||
}
|
||||
|
||||
::scheduler::once([s, name, args]()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::vector<scripting::script_value> arguments{};
|
||||
|
||||
for (const auto& arg : args)
|
||||
{
|
||||
arguments.push_back(arg);
|
||||
}
|
||||
|
||||
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
||||
scripting::notify(player, name, arguments);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
}, ::scheduler::pipeline::server);
|
||||
};
|
||||
}
|
||||
|
||||
state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table);
|
||||
state["CoD"] = state["luiglobals"]["CoD"];
|
||||
state["LUI"] = state["luiglobals"]["LUI"];
|
||||
state["Engine"] = state["luiglobals"]["Engine"];
|
||||
state["Game"] = state["luiglobals"]["Game"];
|
||||
|
||||
auto updater_table = sol::table::create(state.lua_state());
|
||||
|
||||
updater_table["relaunch"] = updater::relaunch;
|
||||
|
||||
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
|
||||
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
|
||||
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
|
||||
|
||||
updater_table["startupdatecheck"] = updater::start_update_check;
|
||||
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
|
||||
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
|
||||
updater_table["isupdateavailable"] = updater::is_update_available;
|
||||
|
||||
updater_table["startupdatedownload"] = updater::start_update_download;
|
||||
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
|
||||
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
|
||||
updater_table["cancelupdate"] = updater::cancel_update;
|
||||
updater_table["isrestartrequired"] = updater::is_restart_required;
|
||||
|
||||
updater_table["getlasterror"] = updater::get_last_error;
|
||||
updater_table["getcurrentfile"] = updater::get_current_file;
|
||||
|
||||
state["updater"] = updater_table;
|
||||
|
||||
auto discord_table = sol::table::create(state.lua_state());
|
||||
|
||||
discord_table["respond"] = discord::respond;
|
||||
discord_table["getavatarmaterial"] = discord::get_avatar_material;
|
||||
discord_table["reply"] = sol::table::create(state.lua_state());
|
||||
discord_table["reply"]["yes"] = DISCORD_REPLY_YES;
|
||||
discord_table["reply"]["ignore"] = DISCORD_REPLY_IGNORE;
|
||||
discord_table["reply"]["no"] = DISCORD_REPLY_NO;
|
||||
|
||||
state["discord"] = discord_table;
|
||||
}
|
||||
}
|
||||
|
||||
context::context(std::string data, script_type type)
|
||||
: scheduler_(state_)
|
||||
{
|
||||
this->state_.open_libraries(sol::lib::base,
|
||||
sol::lib::package,
|
||||
sol::lib::io,
|
||||
sol::lib::string,
|
||||
sol::lib::os,
|
||||
sol::lib::math,
|
||||
sol::lib::table);
|
||||
|
||||
setup_json(this->state_);
|
||||
setup_io(this->state_);
|
||||
setup_types(this->state_, this->scheduler_);
|
||||
|
||||
if (type == script_type::file)
|
||||
{
|
||||
this->folder_ = data;
|
||||
|
||||
this->state_["include"] = [this](const std::string& file)
|
||||
{
|
||||
this->load_script(file);
|
||||
};
|
||||
|
||||
sol::function old_require = this->state_["require"];
|
||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
||||
{
|
||||
return old_require(base_path + path);
|
||||
};
|
||||
|
||||
this->state_["scriptdir"] = [this]()
|
||||
{
|
||||
return this->folder_;
|
||||
};
|
||||
|
||||
printf("Loading ui script '%s'\n", this->folder_.data());
|
||||
this->load_script("__init__");
|
||||
}
|
||||
|
||||
if (type == script_type::code)
|
||||
{
|
||||
handle_error(this->state_.safe_script(data, &sol::script_pass_on_error));
|
||||
}
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
this->state_.collect_garbage();
|
||||
this->scheduler_.clear();
|
||||
this->state_ = {};
|
||||
}
|
||||
|
||||
void context::run_frame()
|
||||
{
|
||||
this->scheduler_.run_frame();
|
||||
this->state_.collect_garbage();
|
||||
}
|
||||
|
||||
void context::load_script(const std::string& script)
|
||||
{
|
||||
if (!this->loaded_scripts_.emplace(script).second)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
||||
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4702)
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#define SOL_PRINT_ERRORS 0
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
enum script_type
|
||||
{
|
||||
file,
|
||||
code
|
||||
};
|
||||
|
||||
class context
|
||||
{
|
||||
public:
|
||||
context(std::string folder, script_type type);
|
||||
~context();
|
||||
|
||||
context(context&&) noexcept = delete;
|
||||
context& operator=(context&&) noexcept = delete;
|
||||
|
||||
context(const context&) = delete;
|
||||
context& operator=(const context&) = delete;
|
||||
|
||||
void run_frame();
|
||||
|
||||
private:
|
||||
sol::state state_{};
|
||||
std::string folder_;
|
||||
std::unordered_set<std::string> loaded_scripts_;
|
||||
|
||||
scheduler scheduler_;
|
||||
|
||||
void load_script(const std::string& script);
|
||||
};
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "engine.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
#include "../../../component/filesystem.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
namespace ui_scripting::lua::engine
|
||||
{
|
||||
namespace
|
||||
{
|
||||
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
||||
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
||||
|
||||
auto& get_scripts()
|
||||
{
|
||||
static std::vector<std::unique_ptr<context>> scripts{};
|
||||
return scripts;
|
||||
}
|
||||
|
||||
void load_scripts(const std::string& script_dir)
|
||||
{
|
||||
if (!utils::io::directory_exists(script_dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto scripts = utils::io::list_files(script_dir);
|
||||
|
||||
for (const auto& script : scripts)
|
||||
{
|
||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
||||
{
|
||||
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void load_code(const std::string& code)
|
||||
{
|
||||
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
clear_converted_functions();
|
||||
get_scripts().clear();
|
||||
|
||||
load_code(lui_common);
|
||||
load_code(lui_updater);
|
||||
|
||||
for (const auto& path : filesystem::get_search_paths())
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/");
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/sp/");
|
||||
}
|
||||
else
|
||||
{
|
||||
load_scripts(path + "/ui_scripts/mp/");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
clear_converted_functions();
|
||||
get_scripts().clear();
|
||||
}
|
||||
|
||||
void run_frame()
|
||||
{
|
||||
for (auto& script : get_scripts())
|
||||
{
|
||||
script->run_frame();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
namespace ui_scripting::lua::engine
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
void run_frame();
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "error.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
void handle_error(const sol::protected_function_result& result)
|
||||
{
|
||||
if (!result.valid())
|
||||
{
|
||||
printf("************** UI Script execution error **************\n");
|
||||
|
||||
const sol::error err = result;
|
||||
printf("%s\n", err.what());
|
||||
|
||||
printf("****************************************************\n");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
void handle_error(const sol::protected_function_result& result);
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
#include "std_include.hpp"
|
||||
#include "context.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
scheduler::scheduler(sol::state& state)
|
||||
{
|
||||
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
||||
|
||||
task_handle_type["clear"] = [this](const task_handle& handle)
|
||||
{
|
||||
this->remove(handle);
|
||||
};
|
||||
}
|
||||
|
||||
void scheduler::run_frame()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
this->merge_callbacks();
|
||||
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->last_call;
|
||||
|
||||
if (diff < i->delay)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_call = now;
|
||||
|
||||
if (!i->is_deleted)
|
||||
{
|
||||
handle_error(i->callback());
|
||||
}
|
||||
|
||||
if (i->is_volatile || i->is_deleted)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scheduler::clear()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
new_tasks.clear();
|
||||
tasks.clear();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
||||
const bool is_volatile)
|
||||
{
|
||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
||||
const bool is_volatile)
|
||||
{
|
||||
const uint64_t id = ++this->current_task_id_;
|
||||
|
||||
task task;
|
||||
task.is_volatile = is_volatile;
|
||||
task.callback = callback;
|
||||
task.delay = delay;
|
||||
task.last_call = std::chrono::steady_clock::now();
|
||||
task.id = id;
|
||||
task.is_deleted = false;
|
||||
|
||||
new_callbacks_.access([&task](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
|
||||
return {id};
|
||||
}
|
||||
|
||||
void scheduler::remove(const task_handle& handle)
|
||||
{
|
||||
auto mask_as_deleted = [&](task_list& tasks)
|
||||
{
|
||||
for (auto& task : tasks)
|
||||
{
|
||||
if (task.id == handle.id)
|
||||
{
|
||||
task.is_deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
callbacks_.access(mask_as_deleted);
|
||||
new_callbacks_.access(mask_as_deleted);
|
||||
}
|
||||
|
||||
void scheduler::merge_callbacks()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
class context;
|
||||
|
||||
class task_handle
|
||||
{
|
||||
public:
|
||||
uint64_t id = 0;
|
||||
};
|
||||
|
||||
class task final : public task_handle
|
||||
{
|
||||
public:
|
||||
std::chrono::steady_clock::time_point last_call{};
|
||||
sol::protected_function callback{};
|
||||
std::chrono::milliseconds delay{};
|
||||
bool is_volatile = false;
|
||||
bool is_deleted = false;
|
||||
};
|
||||
|
||||
class scheduler final
|
||||
{
|
||||
public:
|
||||
scheduler(sol::state& state);
|
||||
|
||||
scheduler(scheduler&&) noexcept = delete;
|
||||
scheduler& operator=(scheduler&&) noexcept = delete;
|
||||
|
||||
scheduler(const scheduler&) = delete;
|
||||
scheduler& operator=(const scheduler&) = delete;
|
||||
|
||||
void run_frame();
|
||||
void clear();
|
||||
|
||||
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
||||
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
||||
|
||||
private:
|
||||
using task_list = std::vector<task>;
|
||||
utils::concurrency::container<task_list> new_callbacks_;
|
||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||
std::atomic_int64_t current_task_id_ = 0;
|
||||
|
||||
void remove(const task_handle& handle);
|
||||
void merge_callbacks();
|
||||
};
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
#include <std_include.hpp>
|
||||
#include "value_conversion.hpp"
|
||||
#include "../execution.hpp"
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
table convert_table(const sol::table& t)
|
||||
{
|
||||
table res{};
|
||||
|
||||
t.for_each([res](const sol::object& key, const sol::object& value)
|
||||
{
|
||||
res.set(convert(key), convert(value));
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
script_value convert_function(const sol::protected_function& function)
|
||||
{
|
||||
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
|
||||
add_converted_function(closure, function);
|
||||
|
||||
game::hks::HksObject value{};
|
||||
value.t = game::hks::TCFUNCTION;
|
||||
value.v.cClosure = closure;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
script_value convert(const sol::lua_value& value)
|
||||
{
|
||||
if (value.is<bool>())
|
||||
{
|
||||
return {value.as<bool>()};
|
||||
}
|
||||
|
||||
if (value.is<int>())
|
||||
{
|
||||
return {value.as<int>()};
|
||||
}
|
||||
|
||||
if (value.is<unsigned int>())
|
||||
{
|
||||
return {value.as<unsigned int>()};
|
||||
}
|
||||
|
||||
if (value.is<double>())
|
||||
{
|
||||
return {value.as<double>()};
|
||||
}
|
||||
|
||||
if (value.is<float>())
|
||||
{
|
||||
return {value.as<float>()};
|
||||
}
|
||||
|
||||
if (value.is<std::string>())
|
||||
{
|
||||
return {value.as<std::string>()};
|
||||
}
|
||||
|
||||
if (value.is<lightuserdata>())
|
||||
{
|
||||
return {value.as<lightuserdata>()};
|
||||
}
|
||||
|
||||
if (value.is<userdata>())
|
||||
{
|
||||
return {value.as<userdata>()};
|
||||
}
|
||||
|
||||
if (value.is<table>())
|
||||
{
|
||||
return {value.as<table>()};
|
||||
}
|
||||
|
||||
if (value.is<function>())
|
||||
{
|
||||
return {value.as<function>()};
|
||||
}
|
||||
|
||||
if (value.is<sol::table>())
|
||||
{
|
||||
return {convert_table(value.as<sol::table>())};
|
||||
}
|
||||
|
||||
if (value.is<sol::protected_function>())
|
||||
{
|
||||
return {convert_function(value.as<sol::protected_function>())};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
sol::lua_value convert(lua_State* state, const script_value& value)
|
||||
{
|
||||
if (value.is<int>())
|
||||
{
|
||||
return {state, value.as<int>()};
|
||||
}
|
||||
|
||||
if (value.is<float>())
|
||||
{
|
||||
return {state, value.as<float>()};
|
||||
}
|
||||
|
||||
if (value.is<bool>())
|
||||
{
|
||||
return {state, value.as<bool>()};
|
||||
}
|
||||
|
||||
if (value.is<std::string>())
|
||||
{
|
||||
return {state, value.as<std::string>()};
|
||||
}
|
||||
|
||||
if (value.is<lightuserdata>())
|
||||
{
|
||||
return {state, value.as<lightuserdata>()};
|
||||
}
|
||||
|
||||
if (value.is<userdata>())
|
||||
{
|
||||
return {state, value.as<userdata>()};
|
||||
}
|
||||
|
||||
if (value.is<table>())
|
||||
{
|
||||
return {state, value.as<table>()};
|
||||
}
|
||||
|
||||
if (value.is<function>())
|
||||
{
|
||||
return {state, value.as<function>()};
|
||||
}
|
||||
|
||||
return {state, sol::lua_nil};
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
#pragma once
|
||||
#include "context.hpp"
|
||||
#include "../script_value.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
script_value convert(const sol::lua_value& value);
|
||||
sol::lua_value convert(lua_State* state, const script_value& value);
|
||||
}
|
@ -2,9 +2,79 @@
|
||||
#include "execution.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
#include "../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
hks_object::hks_object(const game::hks::HksObject& value)
|
||||
{
|
||||
this->assign(value);
|
||||
}
|
||||
|
||||
hks_object::hks_object(const hks_object& other) noexcept
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
hks_object::hks_object(hks_object&& other) noexcept
|
||||
{
|
||||
this->operator=(std::move(other));
|
||||
}
|
||||
|
||||
hks_object& hks_object::operator=(const hks_object& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->assign(other.value_);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
hks_object& hks_object::operator=(hks_object&& other) noexcept
|
||||
{
|
||||
if (this != &other)
|
||||
{
|
||||
this->release();
|
||||
this->value_ = other.value_;
|
||||
other.value_.t = game::hks::TNONE;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
hks_object::~hks_object()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
const game::hks::HksObject& hks_object::get() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
|
||||
void hks_object::assign(const game::hks::HksObject& value)
|
||||
{
|
||||
this->value_ = value;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(this->value_);
|
||||
this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void hks_object::release()
|
||||
{
|
||||
if (this->ref_)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_);
|
||||
this->value_.t = game::hks::TNONE;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Constructors
|
||||
**************************************************************/
|
||||
@ -32,6 +102,24 @@ namespace ui_scripting
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const long long value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TUI64;
|
||||
obj.v.i64 = value;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const unsigned long long value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TUI64;
|
||||
obj.v.ui64 = value;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const bool value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
@ -55,46 +143,68 @@ namespace ui_scripting
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value)
|
||||
script_value::script_value(const char* value, const size_t len)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
if (state == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value));
|
||||
const auto top = state->m_apistack.top;
|
||||
game::hks::hksi_lua_pushlstring(state, value, static_cast<unsigned int>(len));
|
||||
obj = state->m_apistack.top[-1];
|
||||
state->m_apistack.top = top;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value)
|
||||
: script_value(value, strlen(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const std::string& value)
|
||||
: script_value(value.data())
|
||||
: script_value(value.data(), value.size())
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const lightuserdata& value)
|
||||
{
|
||||
this->value_.t = game::hks::TLIGHTUSERDATA;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TLIGHTUSERDATA;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const userdata& value)
|
||||
{
|
||||
this->value_.t = game::hks::TUSERDATA;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TUSERDATA;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const table& value)
|
||||
{
|
||||
this->value_.t = game::hks::TTABLE;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TTABLE;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const function& value)
|
||||
{
|
||||
this->value_.t = value.type;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = value.type;
|
||||
obj.v.ptr = value.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
@ -126,6 +236,34 @@ namespace ui_scripting
|
||||
return static_cast<unsigned int>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Integer 64
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<long long>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TUI64;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<unsigned long long>() const
|
||||
{
|
||||
return this->is<long long>();
|
||||
}
|
||||
|
||||
template <>
|
||||
long long script_value::get() const
|
||||
{
|
||||
return static_cast<long long>(this->get_raw().v.ui64);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned long long script_value::get() const
|
||||
{
|
||||
return static_cast<unsigned long long>(this->get_raw().v.ui64);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Boolean
|
||||
**************************************************************/
|
||||
@ -260,7 +398,7 @@ namespace ui_scripting
|
||||
template <>
|
||||
function script_value::get() const
|
||||
{
|
||||
return { this->get_raw().v.cClosure, this->get_raw().t };
|
||||
return {this->get_raw().v.cClosure, this->get_raw().t};
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
@ -269,6 +407,43 @@ namespace ui_scripting
|
||||
|
||||
const game::hks::HksObject& script_value::get_raw() const
|
||||
{
|
||||
return this->value_;
|
||||
return this->value_.get();
|
||||
}
|
||||
|
||||
bool script_value::operator==(const script_value& other) const
|
||||
{
|
||||
if (this->get_raw().t != other.get_raw().t)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this->get_raw().t == game::hks::TSTRING)
|
||||
{
|
||||
return this->get<std::string>() == other.get<std::string>();
|
||||
}
|
||||
|
||||
return this->get_raw().v.native == other.get_raw().v.native;
|
||||
}
|
||||
|
||||
arguments script_value::operator()() const
|
||||
{
|
||||
return this->as<function>()();
|
||||
}
|
||||
|
||||
arguments script_value::operator()(const arguments& arguments) const
|
||||
{
|
||||
return this->as<function>()(arguments);
|
||||
}
|
||||
|
||||
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
|
||||
: values_(args)
|
||||
, value_(value)
|
||||
, index_(index)
|
||||
{
|
||||
}
|
||||
|
||||
function_arguments::function_arguments(const arguments& values)
|
||||
: values_(values)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,92 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata;
|
||||
class userdata_value;
|
||||
class userdata;
|
||||
class table_value;
|
||||
class table;
|
||||
class function;
|
||||
class script_value;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
std::string get_typename()
|
||||
{
|
||||
auto& info = typeid(T);
|
||||
|
||||
if (info == typeid(std::string) ||
|
||||
info == typeid(const char*))
|
||||
{
|
||||
return "string";
|
||||
}
|
||||
|
||||
if (info == typeid(lightuserdata))
|
||||
{
|
||||
return "lightuserdata";
|
||||
}
|
||||
|
||||
if (info == typeid(userdata))
|
||||
{
|
||||
return "userdata";
|
||||
}
|
||||
|
||||
if (info == typeid(table))
|
||||
{
|
||||
return "table";
|
||||
}
|
||||
|
||||
if (info == typeid(function))
|
||||
{
|
||||
return "function";
|
||||
}
|
||||
|
||||
if (info == typeid(int) ||
|
||||
info == typeid(float) ||
|
||||
info == typeid(unsigned int))
|
||||
{
|
||||
return "number";
|
||||
}
|
||||
|
||||
if (info == typeid(bool))
|
||||
{
|
||||
return "boolean";
|
||||
}
|
||||
|
||||
return info.name();
|
||||
}
|
||||
}
|
||||
|
||||
class hks_object
|
||||
{
|
||||
public:
|
||||
hks_object() = default;
|
||||
hks_object(const game::hks::HksObject& value);
|
||||
hks_object(const hks_object& other) noexcept;
|
||||
hks_object(hks_object&& other) noexcept;
|
||||
|
||||
hks_object& operator=(const hks_object& other) noexcept;
|
||||
hks_object& operator=(hks_object&& other) noexcept;
|
||||
|
||||
~hks_object();
|
||||
|
||||
const game::hks::HksObject& get() const;
|
||||
|
||||
private:
|
||||
void assign(const game::hks::HksObject& value);
|
||||
void release();
|
||||
|
||||
game::hks::HksObject value_{game::hks::TNONE, {}};
|
||||
int ref_{};
|
||||
};
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
using event_arguments = std::unordered_map<std::string, script_value>;
|
||||
|
||||
class script_value
|
||||
{
|
||||
@ -16,12 +96,15 @@ namespace ui_scripting
|
||||
|
||||
script_value(int value);
|
||||
script_value(unsigned int value);
|
||||
script_value(long long value);
|
||||
script_value(unsigned long long value);
|
||||
script_value(bool value);
|
||||
|
||||
script_value(float value);
|
||||
script_value(double value);
|
||||
|
||||
script_value(const char* value);
|
||||
script_value(const char* value, const size_t len);
|
||||
script_value(const std::string& value);
|
||||
|
||||
script_value(const lightuserdata& value);
|
||||
@ -29,6 +112,53 @@ namespace ui_scripting
|
||||
script_value(const table& value);
|
||||
script_value(const function& value);
|
||||
|
||||
template <template<class, class> class C, class T, typename TableType = table>
|
||||
script_value(const C<T, std::allocator<T>>& container)
|
||||
{
|
||||
TableType table_{};
|
||||
int index = 1;
|
||||
|
||||
for (const auto& value : container)
|
||||
{
|
||||
table_.set(index++, value);
|
||||
}
|
||||
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TTABLE;
|
||||
obj.v.ptr = table_.ptr;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
script_value(F f)
|
||||
: script_value(function(f))
|
||||
{
|
||||
}
|
||||
|
||||
bool operator==(const script_value& other) const;
|
||||
|
||||
arguments operator()() const;
|
||||
arguments operator()(const arguments& arguments) const;
|
||||
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->as<function>().call({arguments...});
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
table_value operator[](const char(&key)[Size]) const
|
||||
{
|
||||
return {this->as<table>(), key};
|
||||
}
|
||||
|
||||
template <typename T = script_value>
|
||||
table_value operator[](const T& key) const
|
||||
{
|
||||
return {this->as<table>(), key};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool is() const;
|
||||
|
||||
@ -37,21 +167,93 @@ namespace ui_scripting
|
||||
{
|
||||
if (!this->is<T>())
|
||||
{
|
||||
throw std::runtime_error("Invalid type");
|
||||
const auto hks_typename = game::hks::typenames[this->get_raw().t + 2];
|
||||
const auto typename_ = get_typename<T>();
|
||||
|
||||
throw std::runtime_error(utils::string::va("%s expected, got %s",
|
||||
typename_.data(), hks_typename));
|
||||
}
|
||||
|
||||
return get<T>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T() const
|
||||
{
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
const game::hks::HksObject& get_raw() const;
|
||||
|
||||
hks_object value_{};
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T get() const;
|
||||
|
||||
game::hks::HksObject value_{};
|
||||
};
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
using event_arguments = std::unordered_map<std::string, script_value>;
|
||||
class variadic_args : public arguments
|
||||
{
|
||||
};
|
||||
|
||||
class function_argument
|
||||
{
|
||||
public:
|
||||
function_argument(const arguments& args, const script_value& value, const int index);
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
try
|
||||
{
|
||||
return this->value_.as<T>();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(utils::string::va("bad argument #%d (%s)",
|
||||
this->index_ + 1, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
variadic_args as() const
|
||||
{
|
||||
variadic_args args{};
|
||||
for (auto i = this->index_; i < this->values_.size(); i++)
|
||||
{
|
||||
args.push_back(this->values_[i]);
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
operator T() const
|
||||
{
|
||||
return this->as<T>();
|
||||
}
|
||||
|
||||
private:
|
||||
arguments values_{};
|
||||
script_value value_{};
|
||||
int index_{};
|
||||
};
|
||||
|
||||
class function_arguments
|
||||
{
|
||||
public:
|
||||
function_arguments(const arguments& values);
|
||||
|
||||
function_argument operator[](const int index) const
|
||||
{
|
||||
if (index >= values_.size())
|
||||
{
|
||||
return {values_, {}, index};
|
||||
}
|
||||
|
||||
return {values_, values_[index], index};
|
||||
}
|
||||
private:
|
||||
arguments values_{};
|
||||
};
|
||||
}
|
||||
|
@ -1,350 +1,355 @@
|
||||
#include <std_include.hpp>
|
||||
#include "types.hpp"
|
||||
#include "execution.hpp"
|
||||
#include "../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
lightuserdata::lightuserdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
}
|
||||
|
||||
bool lightuserdata::operator==(const lightuserdata& other)
|
||||
{
|
||||
return this->ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool lightuserdata::operator!=(const lightuserdata& other)
|
||||
{
|
||||
return this->ptr != other.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
userdata::userdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
userdata::userdata(const userdata& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
userdata::userdata(userdata&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
userdata::~userdata()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(const userdata& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(userdata&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool userdata::operator==(const userdata& other)
|
||||
{
|
||||
return this->ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool userdata::operator!=(const userdata& other)
|
||||
{
|
||||
return this->ptr != other.ptr;
|
||||
}
|
||||
|
||||
void userdata::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.ptr = this->ptr;
|
||||
value.t = game::hks::TUSERDATA;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void userdata::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value userdata::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
table::table()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(game::hks::HashTable* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(const table& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
table::table(table&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
table::~table()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
table& table::operator=(const table& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
table& table::operator=(table&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool table::operator==(const table& other)
|
||||
{
|
||||
return this->ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool table::operator!=(const table& other)
|
||||
{
|
||||
return this->ptr != other.ptr;
|
||||
}
|
||||
|
||||
void table::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.table = this->ptr;
|
||||
value.t = game::hks::TTABLE;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void table::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void table::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value table::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||
: ptr(ptr_)
|
||||
, type(type_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(const function& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
function::function(function&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
function::~function()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
function& function::operator=(const function& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
function& function::operator=(function&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool function::operator==(const function& other)
|
||||
{
|
||||
return this->ptr == other.ptr;
|
||||
}
|
||||
|
||||
bool function::operator!=(const function& other)
|
||||
{
|
||||
return this->ptr != other.ptr;
|
||||
}
|
||||
|
||||
void function::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.cClosure = this->ptr;
|
||||
value.t = this->type;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void function::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
arguments function::call(const arguments& arguments) const
|
||||
{
|
||||
return call_script_function(*this, arguments);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Stack
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
stack::stack()
|
||||
lightuserdata::lightuserdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->state = *game::hks::lua_state;
|
||||
this->state->m_apistack.top = this->state->m_apistack.base;
|
||||
}
|
||||
|
||||
void stack::save(int num_args)
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
userdata::userdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->num_args_ = num_args;
|
||||
this->num_calls_ = state->m_numberOfCCalls;
|
||||
this->base_bottom_ = state->m_apistack.base - state->m_apistack.bottom;
|
||||
this->top_bottom_ = state->m_apistack.top - state->m_apistack.bottom;
|
||||
this->callstack_ = state->m_callStack.m_current - state->m_callStack.m_records;
|
||||
this->add();
|
||||
}
|
||||
|
||||
void stack::fix()
|
||||
userdata::userdata(const userdata& other)
|
||||
{
|
||||
this->state->m_numberOfCCalls = this->num_calls_;
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
game::hks::closePendingUpvalues(this->state, &this->state->m_apistack.bottom[this->top_bottom_ - this->num_args_]);
|
||||
this->state->m_callStack.m_current = &this->state->m_callStack.m_records[this->callstack_];
|
||||
userdata::userdata(userdata&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
this->state->m_apistack.base = &this->state->m_apistack.bottom[this->base_bottom_];
|
||||
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ - static_cast<uint64_t>(this->num_args_ + 1)];
|
||||
userdata::~userdata()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
this->state->m_apistack.bottom[this->top_bottom_].t = this->state->m_apistack.top[-1].t;
|
||||
this->state->m_apistack.bottom[this->top_bottom_].v.ptr = this->state->m_apistack.top[-1].v.ptr;
|
||||
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ + 1];
|
||||
userdata& userdata::operator=(const userdata& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(userdata&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void userdata::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.ptr = this->ptr;
|
||||
value.t = game::hks::TUSERDATA;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void userdata::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value userdata::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
userdata_value userdata::operator[](const script_value& key) const
|
||||
{
|
||||
return {*this, key};
|
||||
}
|
||||
|
||||
userdata_value::userdata_value(const userdata& table, const script_value& key)
|
||||
: userdata_(table)
|
||||
, key_(key)
|
||||
{
|
||||
this->value_ = this->userdata_.get(key).get_raw();
|
||||
}
|
||||
|
||||
void userdata_value::operator=(const script_value& value)
|
||||
{
|
||||
this->userdata_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
bool userdata_value::operator==(const script_value& value)
|
||||
{
|
||||
return this->userdata_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
table::table()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(game::hks::HashTable* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(const table& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
table::table(table&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
table::~table()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
table& table::operator=(const table& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
table& table::operator=(table&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void table::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.table = this->ptr;
|
||||
value.t = game::hks::TTABLE;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void table::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void table::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
table_value table::operator[](const script_value& key) const
|
||||
{
|
||||
return {*this, key};
|
||||
}
|
||||
|
||||
script_value table::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
table_value::table_value(const table& table, const script_value& key)
|
||||
: table_(table)
|
||||
, key_(key)
|
||||
{
|
||||
this->value_ = this->table_.get(key).get_raw();
|
||||
}
|
||||
|
||||
void table_value::operator=(const script_value& value)
|
||||
{
|
||||
this->table_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
void table_value::operator=(const table_value& value)
|
||||
{
|
||||
this->table_.set(this->key_, value);
|
||||
this->value_ = value.get_raw();
|
||||
}
|
||||
|
||||
bool table_value::operator==(const script_value& value)
|
||||
{
|
||||
return this->table_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
bool table_value::operator==(const table_value& value)
|
||||
{
|
||||
return this->table_.get(this->key_) == value;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
function::function(game::hks::lua_function func)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0);
|
||||
this->type = game::hks::HksObjectType::TCFUNCTION;
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||
: ptr(ptr_)
|
||||
, type(type_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(const function& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
function::function(function&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
function::~function()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
function& function::operator=(const function& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
function& function::operator=(function&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void function::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.cClosure = this->ptr;
|
||||
value.t = this->type;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto top = state->m_apistack.top;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
state->m_apistack.top = top;
|
||||
}
|
||||
|
||||
void function::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
arguments function::call(const arguments& arguments) const
|
||||
{
|
||||
return call_script_function(*this, arguments);
|
||||
}
|
||||
|
||||
arguments function::operator()(const arguments& arguments) const
|
||||
{
|
||||
return this->call(arguments);
|
||||
}
|
||||
|
||||
arguments function::operator()() const
|
||||
{
|
||||
return this->call({});
|
||||
}
|
||||
}
|
||||
|
@ -1,126 +1,140 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "script_value.hpp"
|
||||
#include "../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata
|
||||
{
|
||||
public:
|
||||
lightuserdata(void*);
|
||||
class lightuserdata
|
||||
{
|
||||
public:
|
||||
lightuserdata(void*);
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
bool operator==(const lightuserdata& other);
|
||||
bool operator!=(const lightuserdata& other);
|
||||
class userdata_value;
|
||||
|
||||
void* ptr;
|
||||
};
|
||||
class userdata
|
||||
{
|
||||
public:
|
||||
userdata(void*);
|
||||
|
||||
class userdata
|
||||
{
|
||||
public:
|
||||
userdata(void*);
|
||||
userdata(const userdata& other);
|
||||
userdata(userdata&& other) noexcept;
|
||||
|
||||
userdata(const userdata& other);
|
||||
userdata(userdata&& other) noexcept;
|
||||
~userdata();
|
||||
|
||||
~userdata();
|
||||
userdata& operator=(const userdata& other);
|
||||
userdata& operator=(userdata&& other) noexcept;
|
||||
|
||||
userdata& operator=(const userdata& other);
|
||||
userdata& operator=(userdata&& other) noexcept;
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
bool operator==(const userdata& other);
|
||||
bool operator!=(const userdata& other);
|
||||
userdata_value operator[](const script_value& key) const;
|
||||
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
void* ptr;
|
||||
|
||||
void* ptr;
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
int ref{};
|
||||
};
|
||||
|
||||
int ref{};
|
||||
};
|
||||
class userdata_value : public script_value
|
||||
{
|
||||
public:
|
||||
userdata_value(const userdata& table, const script_value& key);
|
||||
void operator=(const script_value& value);
|
||||
bool operator==(const script_value& value);
|
||||
private:
|
||||
userdata userdata_;
|
||||
script_value key_;
|
||||
};
|
||||
|
||||
class table
|
||||
{
|
||||
public:
|
||||
table();
|
||||
table(game::hks::HashTable* ptr_);
|
||||
class table_value;
|
||||
|
||||
table(const table& other);
|
||||
table(table&& other) noexcept;
|
||||
class table
|
||||
{
|
||||
public:
|
||||
table();
|
||||
table(game::hks::HashTable* ptr_);
|
||||
|
||||
~table();
|
||||
table(const table& other);
|
||||
table(table&& other) noexcept;
|
||||
|
||||
table& operator=(const table& other);
|
||||
table& operator=(table&& other) noexcept;
|
||||
~table();
|
||||
|
||||
bool operator==(const table& other);
|
||||
bool operator!=(const table& other);
|
||||
table& operator=(const table& other);
|
||||
table& operator=(table&& other) noexcept;
|
||||
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
game::hks::HashTable* ptr;
|
||||
table_value operator[](const script_value& key) const;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
game::hks::HashTable* ptr;
|
||||
|
||||
int ref{};
|
||||
};
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
class function
|
||||
{
|
||||
public:
|
||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||
int ref{};
|
||||
};
|
||||
|
||||
function(const function& other);
|
||||
function(function&& other) noexcept;
|
||||
class table_value : public script_value
|
||||
{
|
||||
public:
|
||||
table_value(const table& table, const script_value& key);
|
||||
void operator=(const script_value& value);
|
||||
void operator=(const table_value& value);
|
||||
bool operator==(const script_value& value);
|
||||
bool operator==(const table_value& value);
|
||||
private:
|
||||
table table_;
|
||||
script_value key_;
|
||||
};
|
||||
|
||||
~function();
|
||||
class function
|
||||
{
|
||||
public:
|
||||
function(game::hks::lua_function);
|
||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||
|
||||
function& operator=(const function& other);
|
||||
function& operator=(function&& other) noexcept;
|
||||
template <typename F>
|
||||
function(F f)
|
||||
{
|
||||
this->ptr = ui_scripting::convert_function(f);
|
||||
this->type = game::hks::TCFUNCTION;
|
||||
}
|
||||
|
||||
bool operator==(const function& other);
|
||||
bool operator!=(const function& other);
|
||||
function(const function& other);
|
||||
function(function&& other) noexcept;
|
||||
|
||||
arguments call(const arguments& arguments) const;
|
||||
~function();
|
||||
|
||||
game::hks::cclosure* ptr;
|
||||
game::hks::HksObjectType type;
|
||||
function& operator=(const function& other);
|
||||
function& operator=(function&& other) noexcept;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
arguments call(const arguments& arguments) const;
|
||||
|
||||
int ref{};
|
||||
};
|
||||
arguments operator()(const arguments& arguments) const;
|
||||
|
||||
class stack final
|
||||
{
|
||||
public:
|
||||
stack();
|
||||
template<class ...T>
|
||||
arguments operator()(T... arguments) const
|
||||
{
|
||||
return this->call({arguments...});
|
||||
}
|
||||
|
||||
void save(int num_args);
|
||||
void fix();
|
||||
arguments operator()() const;
|
||||
|
||||
stack(stack&&) = delete;
|
||||
stack(const stack&) = delete;
|
||||
stack& operator=(stack&&) = delete;
|
||||
stack& operator=(const stack&) = delete;
|
||||
game::hks::cclosure* ptr;
|
||||
game::hks::HksObjectType type;
|
||||
|
||||
private:
|
||||
game::hks::lua_State* state;
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int num_args_;
|
||||
int num_calls_;
|
||||
|
||||
uint64_t base_bottom_;
|
||||
uint64_t top_bottom_;
|
||||
uint64_t callstack_;
|
||||
};
|
||||
int ref{};
|
||||
};
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ FARPROC loader::load(const utils::nt::library& library, const std::string& buffe
|
||||
return FARPROC(library.get_ptr() + source.get_relative_entry_point());
|
||||
}
|
||||
|
||||
FARPROC loader::load_library(const std::string& filename) const
|
||||
FARPROC loader::load_library(const std::string& filename, uint64_t* base_address) const
|
||||
{
|
||||
const auto target = utils::nt::library::load(filename);
|
||||
if (!target)
|
||||
@ -40,10 +40,7 @@ FARPROC loader::load_library(const std::string& filename) const
|
||||
}
|
||||
|
||||
const auto base = size_t(target.get_ptr());
|
||||
if(base != 0x140000000)
|
||||
{
|
||||
throw std::runtime_error{utils::string::va("Binary was mapped at 0x%llX (instead of 0x%llX). Something is severely broken :(", base, 0x140000000)};
|
||||
}
|
||||
*base_address = base;
|
||||
|
||||
this->load_imports(target, target);
|
||||
this->load_tls(target, target);
|
||||
|
@ -5,7 +5,7 @@ class loader final
|
||||
{
|
||||
public:
|
||||
FARPROC load(const utils::nt::library& library, const std::string& buffer) const;
|
||||
FARPROC load_library(const std::string& filename) const;
|
||||
FARPROC load_library(const std::string& filename, uint64_t* base_address) const;
|
||||
|
||||
void set_import_resolver(const std::function<void*(const std::string&, const std::string&)>& resolver);
|
||||
|
||||
|
@ -20,16 +20,6 @@ BOOL WINAPI system_parameters_info_a(const UINT uiAction, const UINT uiParam, co
|
||||
return SystemParametersInfoA(uiAction, uiParam, pvParam, fWinIni);
|
||||
}
|
||||
|
||||
FARPROC WINAPI get_proc_address(const HMODULE hModule, const LPCSTR lpProcName)
|
||||
{
|
||||
if (lpProcName == "GlobalMemoryStatusEx"s)
|
||||
{
|
||||
component_loader::post_unpack();
|
||||
}
|
||||
|
||||
return GetProcAddress(hModule, lpProcName);
|
||||
}
|
||||
|
||||
launcher::mode detect_mode_from_arguments()
|
||||
{
|
||||
if (utils::flags::has_flag("dedicated"))
|
||||
@ -50,14 +40,52 @@ launcher::mode detect_mode_from_arguments()
|
||||
return launcher::mode::none;
|
||||
}
|
||||
|
||||
FARPROC load_binary(const launcher::mode mode)
|
||||
bool apply_aslr_patch(std::string* data)
|
||||
{
|
||||
// mp binary, sp binary
|
||||
if (data->size() != 0x1B97788 && data->size() != 0x1346D88)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* dos_header = reinterpret_cast<PIMAGE_DOS_HEADER>(&data->at(0));
|
||||
auto* nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(&data->at(dos_header->e_lfanew));
|
||||
auto* optional_header = &nt_headers->OptionalHeader;
|
||||
|
||||
if (optional_header->DllCharacteristics & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE)
|
||||
{
|
||||
optional_header->DllCharacteristics &= ~(IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void get_aslr_patched_binary(std::string* binary, std::string* data)
|
||||
{
|
||||
std::string patched_binary = "h1-mod\\" + *binary;
|
||||
|
||||
if (!apply_aslr_patch(data) ||
|
||||
(!utils::io::file_exists(patched_binary) &&
|
||||
!utils::io::write_file(patched_binary, *data, false)))
|
||||
{
|
||||
throw std::runtime_error(utils::string::va(
|
||||
"Could not create aslr patched binary!\n(%s)",
|
||||
binary->data()
|
||||
));
|
||||
}
|
||||
|
||||
*binary = patched_binary;
|
||||
}
|
||||
|
||||
FARPROC load_binary(const launcher::mode mode, uint64_t* base_address)
|
||||
{
|
||||
loader loader;
|
||||
utils::nt::library self;
|
||||
|
||||
loader.set_import_resolver([self](const std::string& library, const std::string& function) -> void*
|
||||
{
|
||||
if (library == "steam_api64.dll")
|
||||
if (library == "steam_api64.dll"
|
||||
&& function != "SteamAPI_GetSteamInstallPath") // Arxan requires one valid steam api import - maybe SteamAPI_Shutdown is better?
|
||||
{
|
||||
return self.get_proc<FARPROC>(function);
|
||||
}
|
||||
@ -69,10 +97,6 @@ FARPROC load_binary(const launcher::mode mode)
|
||||
{
|
||||
return system_parameters_info_a;
|
||||
}
|
||||
else if (function == "GetProcAddress")
|
||||
{
|
||||
return get_proc_address;
|
||||
}
|
||||
|
||||
return component_loader::load_import(library, function);
|
||||
});
|
||||
@ -100,7 +124,14 @@ FARPROC load_binary(const launcher::mode mode)
|
||||
binary.data()));
|
||||
}
|
||||
|
||||
return loader.load_library(binary);
|
||||
get_aslr_patched_binary(&binary, &data);
|
||||
|
||||
#ifdef INJECT_HOST_AS_LIB
|
||||
return loader.load_library(binary, base_address);
|
||||
#else
|
||||
*base_address = 0x140000000;
|
||||
return loader.load(self, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void remove_crash_file()
|
||||
@ -158,6 +189,8 @@ void apply_proper_directory()
|
||||
|
||||
int main()
|
||||
{
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
|
||||
FARPROC entry_point;
|
||||
enable_dpi_awareness();
|
||||
|
||||
@ -166,6 +199,7 @@ int main()
|
||||
limit_parallel_dll_loading();
|
||||
|
||||
srand(uint32_t(time(nullptr)));
|
||||
remove_crash_file();
|
||||
|
||||
{
|
||||
auto premature_shutdown = true;
|
||||
@ -179,8 +213,7 @@ int main()
|
||||
|
||||
try
|
||||
{
|
||||
apply_proper_directory();
|
||||
remove_crash_file();
|
||||
//apply_proper_directory();
|
||||
|
||||
if (!component_loader::post_start()) return 0;
|
||||
|
||||
@ -194,12 +227,15 @@ int main()
|
||||
|
||||
game::environment::set_mode(mode);
|
||||
|
||||
entry_point = load_binary(mode);
|
||||
uint64_t base_address{};
|
||||
entry_point = load_binary(mode, &base_address);
|
||||
if (!entry_point)
|
||||
{
|
||||
throw std::runtime_error("Unable to load binary into memory");
|
||||
}
|
||||
|
||||
game::base_address = base_address;
|
||||
|
||||
if (!component_loader::post_load()) return 0;
|
||||
|
||||
premature_shutdown = false;
|
||||
|
@ -22,7 +22,7 @@
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
|
@ -1,6 +1,30 @@
|
||||
menucallbacks = {}
|
||||
originalmenus = {}
|
||||
stack = {}
|
||||
local menucallbacks = {}
|
||||
local originalmenus = {}
|
||||
local stack = {}
|
||||
|
||||
function getchildren(element)
|
||||
local children = {}
|
||||
local first = element:getFirstChild()
|
||||
|
||||
while (first) do
|
||||
table.insert(children, first)
|
||||
first = first:getNextSibling()
|
||||
end
|
||||
|
||||
return children
|
||||
end
|
||||
|
||||
function printchildtree(element, indent, last)
|
||||
indent = indent or ""
|
||||
|
||||
print(indent .. "+- " .. element.id .. " " .. (element:getText() or ""))
|
||||
indent = indent .. (last and " " or "| ")
|
||||
|
||||
local children = getchildren(element)
|
||||
for i = 1, #children do
|
||||
printchildtree(children[i], indent, i == #children)
|
||||
end
|
||||
end
|
||||
|
||||
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
|
||||
local oncancel = stack.oncancel
|
||||
@ -14,9 +38,7 @@ LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
|
||||
end
|
||||
})
|
||||
|
||||
local listchildren = popup:getChildById("LUIHorizontalList"):getchildren()
|
||||
local children = listchildren[2]:getchildren()
|
||||
popup.text = children[2]
|
||||
popup.text = popup:getLastChild():getPreviousSibling():getPreviousSibling()
|
||||
|
||||
stack = {
|
||||
ret = popup
|
||||
@ -76,10 +98,10 @@ LUI.onmenuopen = function(name, callback)
|
||||
originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
|
||||
LUI.MenuBuilder.m_types_build[name] = function(...)
|
||||
local args = {...}
|
||||
local menu = originalmenus[name](table.unpack(args))
|
||||
local menu = originalmenus[name](unpack(args))
|
||||
|
||||
for k, v in luiglobals.next, menucallbacks[name] do
|
||||
v(menu, table.unpack(args))
|
||||
for k, v in next, menucallbacks[name] do
|
||||
v(menu, unpack(args))
|
||||
end
|
||||
|
||||
return menu
|
||||
@ -136,7 +158,7 @@ LUI.openpopupmenu = function(menu, args)
|
||||
end
|
||||
|
||||
LUI.yesnopopup = function(data)
|
||||
for k, v in luiglobals.next, data do
|
||||
for k, v in next, data do
|
||||
stack[k] = v
|
||||
end
|
||||
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
|
||||
@ -144,21 +166,9 @@ LUI.yesnopopup = function(data)
|
||||
end
|
||||
|
||||
LUI.confirmationpopup = function(data)
|
||||
for k, v in luiglobals.next, data do
|
||||
for k, v in next, data do
|
||||
stack[k] = v
|
||||
end
|
||||
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
|
||||
return stack.ret
|
||||
end
|
||||
|
||||
function userdata_:getchildren()
|
||||
local children = {}
|
||||
local first = self:getFirstChild()
|
||||
|
||||
while (first) do
|
||||
table.insert(children, first)
|
||||
first = first:getNextSibling()
|
||||
end
|
||||
|
||||
return children
|
||||
end
|
||||
end
|
@ -1,6 +1,23 @@
|
||||
#include <std_include.hpp>
|
||||
|
||||
#pragma comment(linker, "/stack:0x1000000")
|
||||
|
||||
#ifdef INJECT_HOST_AS_LIB
|
||||
#pragma comment(linker, "/base:0x160000000")
|
||||
#else
|
||||
#pragma comment(linker, "/base:0x140000000")
|
||||
#pragma comment(linker, "/merge:.data=.cld")
|
||||
#pragma comment(linker, "/merge:.rdata=.clr")
|
||||
#pragma comment(linker, "/merge:.cl=.main")
|
||||
#pragma comment(linker, "/merge:.text=.main")
|
||||
#endif
|
||||
|
||||
#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language=''\"")
|
||||
|
||||
#ifndef INJECT_HOST_AS_LIB
|
||||
#pragma bss_seg(".payload")
|
||||
char payload_data[BINARY_PAYLOAD_SIZE];
|
||||
#endif
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#define BINARY_PAYLOAD_SIZE 0x12000000
|
||||
#define BINARY_PAYLOAD_SIZE 0x14000000
|
||||
|
||||
// Decide whether to load the game as lib or to inject it
|
||||
#define INJECT_HOST_AS_LIB
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4100)
|
||||
|
@ -50,7 +50,7 @@ namespace steam
|
||||
|
||||
unsigned int utils::GetAppID()
|
||||
{
|
||||
return 209660;
|
||||
return 393100;
|
||||
}
|
||||
|
||||
void utils::SetOverlayNotificationPosition(int eNotificationPosition)
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
|
||||
namespace steam
|
||||
{
|
||||
uint64_t callbacks::call_id_ = 0;
|
||||
@ -82,13 +84,13 @@ namespace steam
|
||||
result_handlers_[result.call]->run(result.data, false, result.call);
|
||||
}
|
||||
|
||||
for (const auto& callback : callback_list_)
|
||||
/*for (const auto& callback : callback_list_)
|
||||
{
|
||||
if (callback && callback->get_i_callback() == result.type)
|
||||
{
|
||||
callback->run(result.data, false, 0);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if (result.data)
|
||||
{
|
||||
@ -108,6 +110,13 @@ namespace steam
|
||||
|
||||
bool SteamAPI_Init()
|
||||
{
|
||||
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
|
||||
if (steam_path.empty()) return true;
|
||||
|
||||
::utils::nt::library::load(steam_path / "tier0_s64.dll");
|
||||
::utils::nt::library::load(steam_path / "vstdlib_s64.dll");
|
||||
::utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll");
|
||||
::utils::nt::library::load(steam_path / "steamclient64.dll");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -235,4 +244,4 @@ namespace steam
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user