diff --git a/.gitmodules b/.gitmodules
index a8f01577..3fa56e25 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,9 +22,6 @@
[submodule "deps/asmjit"]
path = deps/asmjit
url = https://github.com/asmjit/asmjit.git
-[submodule "deps/protobuf"]
- path = deps/protobuf
- url = https://github.com/google/protobuf.git
[submodule "deps/udis86"]
path = deps/udis86
url = https://github.com/vmt/udis86.git
diff --git a/README.md b/README.md
index 3ff4538a..9a5b352c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
[![open bugs](https://img.shields.io/github/issues/fedddddd/h2-mod/bug?label=bugs)](https://github.com/fedddddd/h2-mod/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
[![Build](https://github.com/fedddddd/h2-mod/workflows/Build/badge.svg)](https://github.com/fedddddd/h2-mod/actions)
[![Build status](https://ci.appveyor.com/api/projects/status/0sh80kdnsvm53rno?svg=true)](https://ci.appveyor.com/project/fedddddd/h2-mod)
-
+[![Discord](https://img.shields.io/discord/955362057581129738?color=%237289DA&label=members&logo=discord&logoColor=%23FFFFFF)](https://discord.gg/dpnRn2tKT9)
# H2: Mod
@@ -15,7 +15,7 @@
**NOTE**: Cracked/Pirated versions of the game are NOT compatible with this mod, if you run such a version and have issues/crashes launching the client refer to [this issue](https://github.com/fedddddd/h2-mod/issues/111).
-- **[Click here to get the latest release](https://ci.appveyor.com/api/projects/fedddddd/h2-mod/artifacts/build%2Fbin%2Fx64%2FRelease%2Fh2-mod.exe?branch=develop&job=Environment%3A%20APPVEYOR_BUILD_WORKER_IMAGE%3DVisual%20Studio%202019%2C%20PREMAKE_ACTION%3Dvs2019%2C%20CI%3D1%3B%20Configuration%3A%20Release)**
+- **[Click here to get the latest release](https://ci.appveyor.com/api/projects/fedddddd/h2-mod/artifacts/build%2Fbin%2Fx64%2FRelease%2Fh2-mod.exe?branch=develop&job=Environment%3A%20APPVEYOR_BUILD_WORKER_IMAGE%3DVisual%20Studio%202022%2C%20PREMAKE_ACTION%3Dvs2022%2C%20CI%3D1%3B%20Configuration%3A%20Release)**
- **You will need to drop this in your Call of Duty: Modern Warfare 2 Campaign Remastered installation folder. If you don't have Call of Duty: Modern Warfare 2 Campaign Remastered, get those game files first.**
## Compile from source
diff --git a/data/ui_scripts/branding/__init__.lua b/data/ui_scripts/branding/__init__.lua
index f0338fbc..89368f2d 100644
--- a/data/ui_scripts/branding/__init__.lua
+++ b/data/ui_scripts/branding/__init__.lua
@@ -5,88 +5,88 @@ local extrawidth = 50
local extraheight = 40
LUI.MenuBuilder.m_types_build["SystemInfo"] = function (f6_arg0, f6_arg1)
- local f6_local0 = LUI.MenuTemplate.spMenuOffset
- local title = "LUA_MENU_SYSTEM_INFO_CAPS"
+ local f6_local0 = LUI.MenuTemplate.spMenuOffset
+ local title = "LUA_MENU_SYSTEM_INFO_CAPS"
- local f6_local2 = false
- local f6_local3 = 0
+ local f6_local2 = false
+ local f6_local3 = 0
- local menu = LUI.MenuTemplate.new(f6_arg0, {
- menu_title = title,
- menu_top_indent = f6_local0 + f6_local3,
- showSelectButton = false,
- skipAnim = f6_local2
- })
+ local menu = LUI.MenuTemplate.new(f6_arg0, {
+ menu_title = title,
+ menu_top_indent = f6_local0 + f6_local3,
+ showSelectButton = false,
+ skipAnim = f6_local2
+ })
- menu:setClass(LUI.SystemInfo)
- menu:PopulateMissingProps(f6_arg1)
- menu:ValidateProps(f6_arg1)
- menu.id = "systemInfo_id"
+ menu:setClass(LUI.SystemInfo)
+ menu:PopulateMissingProps(f6_arg1)
+ menu:ValidateProps(f6_arg1)
+ menu.id = "systemInfo_id"
- local f6_local5 = 300
- local f6_local6 = LUI.MenuTemplate.ListTop + f6_local0
- local f6_local7 = f6_arg1.menu_height
- if not f6_local7 then
- f6_local7 = f6_local5
- end
+ local f6_local5 = 300
+ local f6_local6 = LUI.MenuTemplate.ListTop + f6_local0
+ local f6_local7 = f6_arg1.menu_height
+ if not f6_local7 then
+ f6_local7 = f6_local5
+ end
- f6_local7 = f6_local7 + f6_local6 - extraheight
+ f6_local7 = f6_local7 + f6_local6 - extraheight
- local f6_local9 = luiglobals.GenericMenuDims.OptionMenuWidth + 100
- local f6_local10 = menu.properties
- local topoffset2 = LUI.MenuTemplate.ListTop + LUI.MenuTemplate.spMenuOffset
+ local f6_local9 = luiglobals.GenericMenuDims.OptionMenuWidth + 100
+ local f6_local10 = menu.properties
+ local topoffset2 = LUI.MenuTemplate.ListTop + LUI.MenuTemplate.spMenuOffset
- local decobox = LUI.MenuBuilder.BuildRegisteredType("h1_box_deco", {
- decoTopOffset = topoffset2 - topoffset + 15,
- decoBottomOffset = -f6_local7,
- decoRightOffset = -665 + extrawidth
- })
+ local decobox = LUI.MenuBuilder.BuildRegisteredType("h1_box_deco", {
+ decoTopOffset = topoffset2 - topoffset + 15,
+ decoBottomOffset = -f6_local7,
+ decoRightOffset = -665 + extrawidth
+ })
- menu:addElement(decobox)
+ menu:addElement(decobox)
- local decoleft = CoD.CreateState(0, 0.5, 8, 0.5, CoD.AnchorTypes.TopLeft)
- decoleft.color = luiglobals.Colors.h1.light_grey
- decobox:addElement(LUI.UILine.new(decoleft))
+ local decoleft = CoD.CreateState(0, 0.5, 8, 0.5, CoD.AnchorTypes.TopLeft)
+ decoleft.color = luiglobals.Colors.h1.light_grey
+ decobox:addElement(LUI.UILine.new(decoleft))
- local decoright = CoD.CreateState(0, 0.5, -8, 0.5, CoD.AnchorTypes.TopRight)
- decoright.color = luiglobals.Colors.h1.light_grey
- decobox:addElement(LUI.UILine.new(decoright))
+ local decoright = CoD.CreateState(0, 0.5, -8, 0.5, CoD.AnchorTypes.TopRight)
+ decoright.color = luiglobals.Colors.h1.light_grey
+ decobox:addElement(LUI.UILine.new(decoright))
- local element = LUI.UIVerticalList.new({
- leftAnchor = true,
- rightAnchor = true,
- topAnchor = true,
- bottomAnchor = true,
- left = spacing,
- right = 100,
- top = topoffset2 + 15,
- bottom = 0,
- spacing = spacing * 0.8
- })
+ local element = LUI.UIVerticalList.new({
+ leftAnchor = true,
+ rightAnchor = true,
+ topAnchor = true,
+ bottomAnchor = true,
+ left = spacing,
+ right = 100,
+ top = topoffset2 + 15,
+ bottom = 0,
+ spacing = spacing * 0.8
+ })
- element.id = "systemInfoList_id"
- menu.vlist = element
- menu:addElement(element)
+ element.id = "systemInfoList_id"
+ menu.vlist = element
+ menu:addElement(element)
- local optionmenuwidth = luiglobals.GenericMenuDims.OptionMenuWidth
- luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth + extrawidth
+ local optionmenuwidth = luiglobals.GenericMenuDims.OptionMenuWidth
+ luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth + extrawidth
- menu:AddInfo(Engine.Localize("MENU_SYSINFO_VERSION"), function()
- return Engine.GetBuildNumber()
- end)
+ menu:AddInfo(Engine.Localize("MENU_SYSINFO_VERSION"), function()
+ return Engine.GetBuildNumber()
+ end)
- menu:AddInfo(Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_LINK"), function()
- return Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_URL")
- end)
+ menu:AddInfo(Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_LINK"), function()
+ return Engine.Localize("MENU_SYSINFO_CUSTOMER_SUPPORT_URL")
+ end)
- menu:AddInfo(Engine.Localize("MENU_SYSINFO_DONATION_LINK"), function()
- return Engine.Localize("MENU_SYSINFO_DONATION_URL")
- end)
+ menu:AddInfo(Engine.Localize("MENU_SYSINFO_DONATION_LINK"), function()
+ return Engine.Localize("MENU_SYSINFO_DONATION_URL")
+ end)
- luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth
+ luiglobals.GenericMenuDims.OptionMenuWidth = optionmenuwidth
- menu:AddBackButton()
- menu:registerEventHandler("menu_close", LUI.SystemInfo.LeaveMenu)
+ menu:AddBackButton()
+ menu:registerEventHandler("menu_close", LUI.SystemInfo.LeaveMenu)
- return menu
+ return menu
end
\ No newline at end of file
diff --git a/data/ui_scripts/mods/__init__.lua b/data/ui_scripts/mods/__init__.lua
index e591a736..ca2d3da9 100644
--- a/data/ui_scripts/mods/__init__.lua
+++ b/data/ui_scripts/mods/__init__.lua
@@ -1 +1 @@
-require("loading")
\ No newline at end of file
+require("loading")
diff --git a/data/ui_scripts/mods/loading.lua b/data/ui_scripts/mods/loading.lua
index 18fab2c8..c83ea272 100644
--- a/data/ui_scripts/mods/loading.lua
+++ b/data/ui_scripts/mods/loading.lua
@@ -1,3 +1,14 @@
+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: ^3&&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( {
leftAnchor = true,
@@ -12,112 +23,102 @@ function createdivider(menu, text)
element.scrollingToNext = true
element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", {
- title_bar_text = Engine.ToUpperCase(Engine.Localize(text))
+ title_bar_text = Engine.ToUpperCase(text)
}))
menu.list:addElement(element)
end
function string:truncate(length)
- if (#self <= length) then
- return self
- end
+ if (#self <= length) then
+ return self
+ end
- return self:sub(1, length - 3) .. "..."
+ return self:sub(1, length - 3) .. "..."
end
LUI.addmenubutton("main_campaign", {
- index = 6,
- text = "$_MODS",
- description = "Load installed mods.",
- callback = function()
- LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
- end
+ index = 6,
+ text = "@MENU_MODS",
+ description = Engine.Localize("@MENU_MODS_DESC"),
+ callback = function()
+ LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
+ end
})
function getmodname(path)
- local name = path
- local desc = "Load " .. name
- local infofile = path .. "/info.json"
+ local name = path
+ 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))
- desc = string.format("%s\nAuthor: %s\nVersion: %s",
- data.description, data.author, data.version)
- name = data.name
- end)
- end
+ if (io.fileexists(infofile)) then
+ pcall(function()
+ local data = json.decode(io.readfile(infofile))
+ desc = Engine.Localize("@LUA_MENU_MOD_DESC",
+ data.description, data.author, data.version)
+ name = data.name
+ end)
+ end
- return name, desc
+ return name, desc
end
LUI.MenuBuilder.m_types_build["mods_menu"] = function(a1)
- local menu = LUI.MenuTemplate.new(a1, {
- menu_title = "$_MODS",
- exclusiveController = 0,
- menu_width = 400,
- menu_top_indent = LUI.MenuTemplate.spMenuOffset,
- showTopRightSmallBar = true
- })
+ local menu = LUI.MenuTemplate.new(a1, {
+ menu_title = "@MENU_MODS",
+ exclusiveController = 0,
+ menu_width = 400,
+ menu_top_indent = LUI.MenuTemplate.spMenuOffset,
+ showTopRightSmallBar = true
+ })
- menu:AddButton("$_OPEN STORE", function()
- if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then
- LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu")
- end
- end, nil, true, nil, {
- desc_text = "Download and install mods."
- })
+ menu:AddButton("@LUA_MENU_OPEN_STORE", function()
+ if (LUI.MenuBuilder.m_types_build["mod_store_menu"]) then
+ LUI.FlowManager.RequestAddMenu(nil, "mod_store_menu")
+ end
+ end, nil, true, nil, {
+ desc_text = Engine.Localize("@LUA_MENU_OPEN_STORE_DESC")
+ })
- local modfolder = game:getloadedmod()
- if (modfolder ~= "") then
- createdivider(menu, "$_Loaded mod: ^3" .. getmodname(modfolder):truncate(20))
+ local modfolder = game:getloadedmod()
+ if (modfolder ~= "") then
+ createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", getmodname(modfolder):truncate(24)))
- menu:AddButton("$_UNLOAD", function()
- game:executecommand("unloadmod")
- end, nil, true, nil, {
- desc_text = "Unload the currently loaded mod."
- })
- end
+ menu:AddButton("@LUA_MENU_UNLOAD", function()
+ game:executecommand("unloadmod")
+ end, nil, true, nil, {
+ desc_text = Engine.Localize("@LUA_MENU_UNLOAD_DESC")
+ })
+ end
- createdivider(menu, "$_Available mods")
+ createdivider(menu, Engine.Localize("@LUA_MENU_AVAILABLE_MODS"))
- if (io.directoryexists("mods")) then
- local mods = io.listfiles("mods/")
- for i = 1, #mods do
- if (io.directoryexists(mods[i]) and not io.directoryisempty(mods[i])) then
- local name, desc = getmodname(mods[i])
+ if (io.directoryexists("mods")) then
+ local mods = io.listfiles("mods/")
+ for i = 1, #mods do
+ if (io.directoryexists(mods[i]) and not io.directoryisempty(mods[i])) then
+ local name, desc = getmodname(mods[i])
- if (mods[i] ~= modfolder) then
- menu:AddButton("$_" .. name, function()
- game:executecommand("loadmod " .. mods[i])
- end, nil, true, nil, {
- desc_text = desc
- })
- end
- end
- end
- end
-
- menu:AddBackButton(function(a1)
- Engine.PlaySound(CoD.SFX.MenuBack)
- LUI.FlowManager.RequestLeaveMenu(a1)
- end)
+ if (mods[i] ~= modfolder) then
+ game:addlocalizedstring(name, name)
+ menu:AddButton(name, function()
+ game:executecommand("loadmod " .. mods[i])
+ end, nil, true, nil, {
+ desc_text = desc
+ })
+ end
+ end
+ end
+ end
+
+ menu:AddBackButton(function(a1)
+ Engine.PlaySound(CoD.SFX.MenuBack)
+ LUI.FlowManager.RequestLeaveMenu(a1)
+ end)
- LUI.Options.InitScrollingList(menu.list, nil)
- menu:CreateBottomDivider()
- menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
+ LUI.Options.InitScrollingList(menu.list, nil)
+ menu:CreateBottomDivider()
+ menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
- return menu
+ return menu
end
-
-local localize = Engine.Localize
-Engine.Localize = function(...)
- local args = {...}
-
- if (type(args[1]) == "string" and args[1]:sub(1, 2) == "$_") then
- return args[1]:sub(3, -1)
- end
-
- return localize(table.unpack(args))
-end
\ No newline at end of file
diff --git a/data/ui_scripts/settings/__init__.lua b/data/ui_scripts/settings/__init__.lua
index 2e3db5bd..00cd8125 100644
--- a/data/ui_scripts/settings/__init__.lua
+++ b/data/ui_scripts/settings/__init__.lua
@@ -1,3 +1,28 @@
+game:addlocalizedstring("MENU_GENERAL", "GENERAL")
+game:addlocalizedstring("MENU_GENERAL_DESC", "Set the client's settings.")
+game:addlocalizedstring("LUA_MENU_AUTO_UPDATE", "Automatic updates")
+game:addlocalizedstring("LUA_MENU_CHECK_UPDATES", "Check for updates")
+game:addlocalizedstring("LUA_MENU_CHECK_UPDATES_DESC", "Check for updates.")
+game:addlocalizedstring("LUA_MENU_DRAWING", "Drawing")
+game:addlocalizedstring("LUA_MENU_UPDATES", "Updates")
+game:addlocalizedstring("LUA_MENU_RENDERING", "Rendering")
+
+game:addlocalizedstring("LUA_MENU_DRAW_FPS", "Draw FPS")
+game:addlocalizedstring("LUA_MENU_DRAW_FPS_DESC", "Enable or disable drawing fps or viewpos on screen.")
+game:addlocalizedstring("LUA_MENU_FPS_ONLY", "FPS only")
+game:addlocalizedstring("LUA_MENU_FPS_AND_VIEWPOS", "FPS and View Pos")
+
+game:addlocalizedstring("LUA_MENU_DRAW_SPEED", "Draw speed")
+game:addlocalizedstring("LUA_MENU_DRAW_SPEED_DESC", "Enable or disable drawing the player speed on screen.")
+
+game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH", "Draw speed graph")
+game:addlocalizedstring("LUA_MENU_DRAW_SPEEDGRAPH_DESC", "Enable or disable the speed graph.")
+
+game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT", "Fullbright")
+game:addlocalizedstring("LUA_MENU_R_FULLBRIGHT_DESC", "Change the fullbright mode.")
+game:addlocalizedstring("LUA_MENU_MODE2", "Mode 2")
+game:addlocalizedstring("LUA_MENU_MODE3", "Mode 3")
+
function createdivider(menu, text)
local element = LUI.UIElement.new({
leftAnchor = true,
@@ -20,8 +45,8 @@ end
LUI.addmenubutton("pc_controls", {
index = 4,
- text = "$_GENERAL",
- description = "Set the client's settings.",
+ text = "@MENU_GENERAL",
+ description = Engine.Localize("@MENU_GENERAL_DESC"),
callback = function()
LUI.FlowManager.RequestAddMenu(nil, "settings_menu")
end
@@ -29,134 +54,118 @@ LUI.addmenubutton("pc_controls", {
LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
local menu = LUI.MenuTemplate.new(a1, {
- menu_title = "$_GENERAL",
+ menu_title = "@MENU_GENERAL",
menu_list_divider_top_offset = -(LUI.H1MenuTab.tabChangeHoldingElementHeight + luiglobals.H1MenuDims.spacing),
menu_width = luiglobals.GenericMenuDims.OptionMenuWidth
})
- Engine.SetDvarFromString("ui_cg_autoUpdate", Engine.GetDvarBool("cg_autoUpdate") and "1" or "0")
- Engine.SetDvarFromString("ui_cg_drawFps", Engine.GetDvarInt("cg_drawFps") .. "")
- Engine.SetDvarFromString("ui_cg_speedGraph", Engine.GetDvarBool("cg_speedGraph") and "1" or "0")
- Engine.SetDvarFromString("ui_cg_drawSpeed", Engine.GetDvarBool("cg_drawSpeed") and "1" or "0")
- Engine.SetDvarFromString("ui_r_fullbright", Engine.GetDvarInt("r_fullbright") .. "")
-
- createdivider(menu, "$_UPDATES")
+ createdivider(menu, "@LUA_MENU_UPDATES")
LUI.Options.CreateOptionButton(
menu,
- "ui_cg_autoUpdate",
- "$_AUTOMATIC UPDATES",
+ "cg_auto_update",
+ "@LUA_MENU_AUTO_UPDATE",
"Enable or disable automatic updates on startup.",
{
{
- text = "$_ENABLED",
- value = "1"
+ text = "@LUA_MENU_ENABLED",
+ value = true
},
{
- text = "$_DISABLED",
- value = "0"
+ text = "@LUA_MENU_DISABLED",
+ value = false
}
- }, nil, nil, function(value)
- Engine.SetDvarBool("cg_autoUpdate", Engine.GetDvarString("ui_cg_autoUpdate") == "1")
- end
+ }
)
- menu:AddButton("$_CHECK FOR UPDATES", function()
+ menu:AddButton("@LUA_MENU_CHECK_UPDATES", function()
LUI.tryupdating(false)
end, nil, true, nil, {
- desc_text = "Check for updates."
+ desc_text = Engine.Localize("@LUA_MENU_CHECK_UPDATES_DESC")
})
- createdivider(menu, "$_DRAWING")
+ createdivider(menu, "@LUA_MENU_DRAWING")
LUI.Options.CreateOptionButton(
menu,
- "ui_cg_drawFps",
- "$_DRAW FPS",
- "Enable or disable drawing fps or viewpos on screen.",
+ "cg_drawFps",
+ "@LUA_MENU_DRAW_FPS",
+ "@LUA_MENU_DRAW_FPS_DESC",
{
{
- text = "$_DISABLED",
- value = "0"
+ text = "@LUA_MENU_DISABLED",
+ value = 0
},
{
- text = "$_FPS ONLY",
- value = "1"
+ text = "@LUA_MENU_FPS_ONLY",
+ value = 1
},
{
- text = "$_FPS AND VIEWPOS",
- value = "2"
+ text = "@LUA_MENU_FPS_AND_VIEWPOS",
+ value = 2
}
- }, nil, nil, function(value)
- Engine.SetDvarInt("cg_drawFps", tonumber(Engine.GetDvarString("ui_cg_drawFps")))
- end
+ }
)
LUI.Options.CreateOptionButton(
menu,
- "ui_cg_drawSpeed",
- "$_DRAW SPEED",
+ "cg_drawSpeed",
+ "@LUA_MENU_DRAW_SPEED",
"Enable or disable drawing the player speed on screen.",
{
{
- text = "$_DISABLED",
- value = "0"
+ text = "@LUA_MENU_ENABLED",
+ value = true
},
{
- text = "$_ENABLED",
- value = "1"
+ text = "@LUA_MENU_DISABLED",
+ value = false
}
- }, nil, nil, function(value)
- Engine.SetDvarBool("cg_drawSpeed", Engine.GetDvarString("ui_cg_drawSpeed") == "1")
- end
+ }
)
LUI.Options.CreateOptionButton(
menu,
- "ui_cg_speedGraph",
- "$_DRAW SPEED GRAPH",
- "Enable or disable the speed graph.",
+ "cg_speedGraph",
+ "@LUA_MENU_DRAW_SPEEDGRAPH",
+ "@LUA_MENU_DRAW_SPEEDGRAPH_DESC",
{
{
- text = "$_DISABLED",
- value = "0"
+ text = "@LUA_MENU_ENABLED",
+ value = true
},
{
- text = "$_ENABLED",
- value = "1"
+ text = "@LUA_MENU_DISABLED",
+ value = false
}
- }, nil, nil, function(value)
- Engine.SetDvarBool("cg_speedGraph", Engine.GetDvarString("ui_cg_speedGraph") == "1")
- end
+ }
)
- createdivider(menu, "$_RENDERING")
+ createdivider(menu, "@LUA_MENU_RENDERING")
LUI.Options.CreateOptionButton(
menu,
- "ui_r_fullbright",
- "$_FULLBRIGHT",
- "Change the fullbright mode.",
+ "r_fullbright",
+ "@LUA_MENU_R_FULLBRIGHT",
+ "@LUA_MENU_R_FULLBRIGHT_DESC",
{
{
- text = "$_DISABLED",
- value = "0"
+ text = "@LUA_MENU_DISABLED",
+ value = 0
},
{
- text = "$_ENABLED",
- value = "1"
+ text = "@LUA_MENU_ENABLED",
+ value = 1
},
{
- text = "$_MODE 2",
- value = "2"
+ text = "@LUA_MENU_MODE2",
+ value = 2
},
{
- text = "$_MODE 3",
- value = "3"
+ text = "@LUA_MENU_MODE3",
+ value = 3
}
- }, nil, nil, function(value)
- Engine.SetDvarInt("r_fullbright", tonumber(Engine.GetDvarString("ui_r_fullbright")))
- end
+ }
)
LUI.Options.InitScrollingList(menu.list, nil)
@@ -165,4 +174,4 @@ LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
menu:AddBackButton()
return menu
-end
\ No newline at end of file
+end
diff --git a/deps/GSL b/deps/GSL
index 4377f6e6..38372367 160000
--- a/deps/GSL
+++ b/deps/GSL
@@ -1 +1 @@
-Subproject commit 4377f6e603c64a86c934f1546aa9db482f2e1a4e
+Subproject commit 383723676cd548d615159701ac3d050f8dd1e128
diff --git a/deps/asmjit b/deps/asmjit
index 28c4d8c5..21a31b8a 160000
--- a/deps/asmjit
+++ b/deps/asmjit
@@ -1 +1 @@
-Subproject commit 28c4d8c528527141955006f09124ce672ddfbe3f
+Subproject commit 21a31b8a338da3341d2b423f85913597b8ec3d63
diff --git a/deps/curl b/deps/curl
index 049f3765..47048e02 160000
--- a/deps/curl
+++ b/deps/curl
@@ -1 +1 @@
-Subproject commit 049f3765c7016a3f7a92b7f88aae6405c49b84fb
+Subproject commit 47048e02878c59367db1d42813f32dcce543eed3
diff --git a/deps/imgui b/deps/imgui
index fc842149..22e9093d 160000
--- a/deps/imgui
+++ b/deps/imgui
@@ -1 +1 @@
-Subproject commit fc84214988b74179b3941d35393c24ee8ba7a794
+Subproject commit 22e9093da37f0443f008b947caa6221724e760d6
diff --git a/deps/libtomcrypt b/deps/libtomcrypt
index 673f5ce2..06a81aeb 160000
--- a/deps/libtomcrypt
+++ b/deps/libtomcrypt
@@ -1 +1 @@
-Subproject commit 673f5ce29015a9bba3c96792920a10601b5b0718
+Subproject commit 06a81aeb227424182125363f7554fad5146d6d2a
diff --git a/deps/libtommath b/deps/libtommath
index 04e9d1e7..66de8642 160000
--- a/deps/libtommath
+++ b/deps/libtommath
@@ -1 +1 @@
-Subproject commit 04e9d1e7a0493910b2eb5e757d623870692ada04
+Subproject commit 66de86426e9cdb88526974c765108f01554af2b0
diff --git a/deps/lua b/deps/lua
index 5d708c3f..8426d9b4 160000
--- a/deps/lua
+++ b/deps/lua
@@ -1 +1 @@
-Subproject commit 5d708c3f9cae12820e415d4f89c9eacbe2ab964b
+Subproject commit 8426d9b4d4df1da3c5b2d759e509ae1c50a86667
diff --git a/deps/premake/protobuf.lua b/deps/premake/protobuf.lua
deleted file mode 100644
index 1c58972c..00000000
--- a/deps/premake/protobuf.lua
+++ /dev/null
@@ -1,60 +0,0 @@
-protobuf = {
- source = path.join(dependencies.basePath, "protobuf"),
-}
-
-function protobuf.import()
- links {
- "protobuf"
- }
-
- protobuf.includes()
-end
-
-function protobuf.includes()
- includedirs {
- path.join(protobuf.source, "src"),
- }
-end
-
-function protobuf.project()
- project "protobuf"
- language "C++"
-
- protobuf.includes()
-
- files {
- path.join(protobuf.source, "src/**.cc"),
- "./src/**.proto",
- }
-
- removefiles {
- path.join(protobuf.source, "src/**/*test.cc"),
- path.join(protobuf.source, "src/google/protobuf/*test*.cc"),
-
- path.join(protobuf.source, "src/google/protobuf/testing/**.cc"),
- path.join(protobuf.source, "src/google/protobuf/compiler/**.cc"),
-
- path.join(protobuf.source, "src/google/protobuf/arena_nc.cc"),
- path.join(protobuf.source, "src/google/protobuf/util/internal/error_listener.cc"),
- path.join(protobuf.source, "**/*_gcc.cc"),
- }
-
- rules {
- "ProtobufCompiler"
- }
-
- defines {
- "_SCL_SECURE_NO_WARNINGS",
- "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS",
- "_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS",
- }
-
- linkoptions {
- "-IGNORE:4221"
- }
-
- warnings "Off"
- kind "StaticLib"
-end
-
-table.insert(dependencies, protobuf)
diff --git a/deps/protobuf b/deps/protobuf
deleted file mode 160000
index 2f7ee91e..00000000
--- a/deps/protobuf
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 2f7ee91e326d95915b63918f968244cfefbc022a
diff --git a/deps/rapidjson b/deps/rapidjson
index e4bde977..8261c1dd 160000
--- a/deps/rapidjson
+++ b/deps/rapidjson
@@ -1 +1 @@
-Subproject commit e4bde977440d4a00f820b6586899e48a972d2493
+Subproject commit 8261c1ddf43f10de00fd8c9a67811d1486b2c784
diff --git a/generate.bat b/generate.bat
index 8d2b86a4..06c74119 100644
--- a/generate.bat
+++ b/generate.bat
@@ -1,4 +1,3 @@
@echo off
git submodule update --init --recursive
-tools\premake5 %* vs2022
-pause
\ No newline at end of file
+tools\premake5 %* vs2022
\ No newline at end of file
diff --git a/src/client/component/binding.cpp b/src/client/component/binding.cpp
index 0eb88f30..e29c2871 100644
--- a/src/client/component/binding.cpp
+++ b/src/client/component/binding.cpp
@@ -118,13 +118,13 @@ namespace binding
void post_unpack() override
{
// write all bindings to config file
- key_write_bindings_to_buffer_hook.create(0x3D3840_b, key_write_bindings_to_buffer_stub);
+ key_write_bindings_to_buffer_hook.create(0x1403D3840, key_write_bindings_to_buffer_stub);
// links a custom command to an index
- utils::hook::jump(0x59AE30_b, key_get_binding_for_cmd_stub, true);
+ utils::hook::jump(0x14059AE30, key_get_binding_for_cmd_stub, true);
// execute custom binds
- cl_execute_key_hook.create(0x3CF1E0_b, &cl_execute_key_stub);
+ cl_execute_key_hook.create(0x1403CF1E0, &cl_execute_key_stub);
}
};
}
diff --git a/src/client/component/branding.cpp b/src/client/component/branding.cpp
index 46f0ad7f..42204839 100644
--- a/src/client/component/branding.cpp
+++ b/src/client/component/branding.cpp
@@ -45,7 +45,7 @@ namespace branding
localized_strings::override("MENU_SYSINFO_DONATION_LINK", "Donation Link:");
localized_strings::override("MENU_SYSINFO_DONATION_URL", "https://paypal.me/fedecek");
- utils::hook::jump(0x33D550_b, get_build_number_stub, true);
+ utils::hook::jump(0x14033D550, get_build_number_stub, true);
}
};
}
diff --git a/src/client/component/chat.cpp b/src/client/component/chat.cpp
deleted file mode 100644
index 4520436a..00000000
--- a/src/client/component/chat.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-#include
-#include "loader/component_loader.hpp"
-
-#include "game/game.hpp"
-#include "game/dvars.hpp"
-
-#include "chat.hpp"
-#include "scheduler.hpp"
-
-#include
-#include
-
-#define chat_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 25)
-
-namespace chat
-{
- namespace
- {
- struct message
- {
- std::string text;
- std::chrono::steady_clock::time_point time;
- };
-
- std::deque history;
-
- float color_white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
-
- float screen_max[2];
-
- void check_resize()
- {
- screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0];
- screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1];
- }
-
- float relative(float value)
- {
- const auto ratio = screen_max[0] / 2560.f;
-
- return value * ratio;
- }
-
- void draw_chat()
- {
- check_resize();
-
- const auto now = std::chrono::high_resolution_clock::now();
-
- for (auto i = 0; i < std::min(15, (int)history.size()); i++)
- {
- if (now - history[i].time > 11s)
- {
- return;
- }
-
- const auto diff = now - history[i].time;
-
- float color[4] = { color_white[0], color_white[1], color_white[2], 1.f };
-
- if (diff > 10.5s)
- {
- const auto milliseconds = (float)(11000 - std::chrono::duration_cast(diff).count());
-
- color[3] = (float)(milliseconds / 500.f);
- }
-
- game::R_AddCmdDrawText(history[i].text.data(), 0x7FFFFFFF, chat_font, relative(15.f), relative(600.f + i * 25), 1.f, 1.f, 0.f, color, 0);
- }
- }
- }
-
- void print(const std::string& msg)
- {
- message m;
- m.text = msg;
- m.time = std::chrono::high_resolution_clock::now();
-
- history.push_front(m);
- }
-
- class component final : public component_interface
- {
- public:
- void post_unpack() override
- {
- scheduler::loop([]()
- {
- draw_chat();
- }, scheduler::pipeline::renderer);
- }
- };
-}
-
-REGISTER_COMPONENT(chat::component)
diff --git a/src/client/component/chat.hpp b/src/client/component/chat.hpp
deleted file mode 100644
index d1428459..00000000
--- a/src/client/component/chat.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-namespace chat
-{
- void print(const std::string& msg);
-}
\ No newline at end of file
diff --git a/src/client/component/command.cpp b/src/client/component/command.cpp
index b5350491..6fba69f6 100644
--- a/src/client/component/command.cpp
+++ b/src/client/component/command.cpp
@@ -9,7 +9,6 @@
#include "command.hpp"
#include "scheduler.hpp"
#include "game_console.hpp"
-#include "chat.hpp"
#include "fastfiles.hpp"
#include
@@ -116,7 +115,9 @@ namespace command
const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end())
+ {
add_raw(name, main_handler);
+ }
handlers[command] = callback;
}
@@ -148,7 +149,7 @@ namespace command
public:
void post_unpack() override
{
- utils::hook::jump(0x5A74F0_b, dvar_command_stub, true);
+ utils::hook::jump(0x1405A74F0, dvar_command_stub, true);
add("quit", game::Quit);
@@ -156,7 +157,7 @@ namespace command
{
const auto map = params.get(1);
- const auto exists = utils::hook::invoke(0x412B50_b, map, 0);
+ const auto exists = utils::hook::invoke(0x140412B50, map, 0);
if (!exists)
{
@@ -165,12 +166,7 @@ namespace command
}
// SV_SpawnServer
- utils::hook::invoke(0x6B3AA0_b, map, 0, 0, 0, 0);
- });
-
- add("say", [](const params& params)
- {
- chat::print(params.join(1));
+ utils::hook::invoke(0x1406B3AA0, map, 0, 0, 0, 0);
});
add("listassetpool", [](const params& params)
@@ -207,11 +203,6 @@ namespace command
}
});
- add("baseAddress", []()
- {
- printf("%p\n", (void*)game::base_address);
- });
-
add("commandDump", []()
{
printf("======== Start command dump =========\n");
@@ -333,8 +324,7 @@ namespace command
}
else if (arg == "health"s)
{
-
- if (params.size() > 3)
+ if (params.size() > 2)
{
const auto amount = atoi(params.get(2));
const auto health = player.get("health").as();
@@ -414,7 +404,7 @@ namespace command
try
{
- const scripting::entity player = scripting::call("getentbynum", {0}).as();
+ const auto player = scripting::call("getentbynum", {0}).as();
if (weapon == "all"s)
{
player.call("takeallweapons");
diff --git a/src/client/component/config.cpp b/src/client/component/config.cpp
deleted file mode 100644
index 26ecc7b6..00000000
--- a/src/client/component/config.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include
-#include "loader/component_loader.hpp"
-
-#include "game/game.hpp"
-#include "game/dvars.hpp"
-
-#include "command.hpp"
-#include "game_console.hpp"
-
-#include
-
-namespace config
-{
- class component final : public component_interface
- {
- public:
- void post_unpack() override
- {
- dvars::register_bool("cg_autoUpdate", true, game::DvarFlags::DVAR_FLAG_SAVED);
- }
- };
-}
-
-REGISTER_COMPONENT(config::component)
diff --git a/src/client/component/exception.cpp b/src/client/component/exception.cpp
index c4f0f1dd..91805379 100644
--- a/src/client/component/exception.cpp
+++ b/src/client/component/exception.cpp
@@ -158,7 +158,6 @@ namespace exception
line("Timestamp: "s + get_timestamp());
line(utils::string::va("Exception: 0x%08X", exceptioninfo->ExceptionRecord->ExceptionCode));
line(utils::string::va("Address: 0x%llX", exceptioninfo->ExceptionRecord->ExceptionAddress));
- line(utils::string::va("Base: 0x%llX", game::base_address));
#pragma warning(push)
#pragma warning(disable: 4996)
diff --git a/src/client/component/fastfiles.cpp b/src/client/component/fastfiles.cpp
index 3b71d881..f0426f56 100644
--- a/src/client/component/fastfiles.cpp
+++ b/src/client/component/fastfiles.cpp
@@ -51,7 +51,7 @@ namespace fastfiles
public:
void post_unpack() override
{
- db_try_load_x_file_internal_hook.create(0x4173B0_b, &db_try_load_x_file_internal);
+ db_try_load_x_file_internal_hook.create(0x1404173B0, &db_try_load_x_file_internal);
command::add("loadzone", [](const command::params& params)
{
diff --git a/src/client/component/filesystem.cpp b/src/client/component/filesystem.cpp
new file mode 100644
index 00000000..cba191cc
--- /dev/null
+++ b/src/client/component/filesystem.cpp
@@ -0,0 +1,56 @@
+#include
+#include "loader/component_loader.hpp"
+
+#include "filesystem.hpp"
+
+#include
+
+namespace filesystem
+{
+ std::unordered_set& get_search_paths()
+ {
+ static std::unordered_set search_paths{};
+ return search_paths;
+ }
+
+ std::string read_file(const std::string& path)
+ {
+ for (const auto& search_path : get_search_paths())
+ {
+ const auto path_ = search_path + "/" + path;
+ if (utils::io::file_exists(path_))
+ {
+ return utils::io::read_file(path_);
+ }
+ }
+
+ return {};
+ }
+
+ bool read_file(const std::string& path, std::string* data)
+ {
+ for (const auto& search_path : get_search_paths())
+ {
+ const auto path_ = search_path + "/" + path;
+ if (utils::io::read_file(path_, data))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ class component final : public component_interface
+ {
+ public:
+ void post_unpack() override
+ {
+ get_search_paths().insert(".");
+ get_search_paths().insert("h2-mod");
+ get_search_paths().insert("data");
+ }
+ };
+}
+
+REGISTER_COMPONENT(filesystem::component)
diff --git a/src/client/component/filesystem.hpp b/src/client/component/filesystem.hpp
new file mode 100644
index 00000000..718e5377
--- /dev/null
+++ b/src/client/component/filesystem.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+namespace filesystem
+{
+ std::unordered_set& get_search_paths();
+ std::string read_file(const std::string& path);
+ bool read_file(const std::string& path, std::string* data);
+}
\ No newline at end of file
diff --git a/src/client/component/fonts.cpp b/src/client/component/fonts.cpp
new file mode 100644
index 00000000..bdd16e53
--- /dev/null
+++ b/src/client/component/fonts.cpp
@@ -0,0 +1,131 @@
+#include
+#include "loader/component_loader.hpp"
+
+#include "fonts.hpp"
+#include "game_console.hpp"
+#include "filesystem.hpp"
+
+#include "game/game.hpp"
+#include "game/dvars.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace fonts
+{
+ namespace
+ {
+ struct font_data_t
+ {
+ std::unordered_map fonts;
+ std::unordered_map raw_fonts;
+ };
+
+ utils::concurrency::container font_data;
+
+ game::TTF* create_font(const std::string& name, const std::string& data)
+ {
+ const auto font = utils::memory::get_allocator()->allocate();
+ font->name = utils::memory::get_allocator()->duplicate_string(name);
+ font->buffer = utils::memory::get_allocator()->duplicate_string(data);
+ font->len = static_cast(data.size());
+ font->fontFace = 0;
+ return font;
+ }
+
+ void free_font(game::TTF* font)
+ {
+ utils::memory::get_allocator()->free(font->buffer);
+ utils::memory::get_allocator()->free(font->name);
+ utils::memory::get_allocator()->free(font);
+ }
+
+ game::TTF* load_font(const std::string& name)
+ {
+ return font_data.access([&](font_data_t& data_) -> game::TTF*
+ {
+ if (const auto i = data_.fonts.find(name); i != data_.fonts.end())
+ {
+ return i->second;
+ }
+
+ std::string data{};
+ if (const auto i = data_.raw_fonts.find(name); i != data_.raw_fonts.end())
+ {
+ data = i->second;
+ }
+
+ if (data.empty() && !filesystem::read_file(name, &data))
+ {
+ return nullptr;
+ }
+
+ const auto material = create_font(name, data);
+ data_.fonts[name] = material;
+
+ return material;
+ });
+ }
+
+ game::TTF* try_load_font(const std::string& name)
+ {
+ try
+ {
+ return load_font(name);
+ }
+ catch (const std::exception& e)
+ {
+ game_console::print(game_console::con_type_error, "Failed to load font %s: %s\n", name.data(), e.what());
+ }
+
+ return nullptr;
+ }
+
+ game::TTF* db_find_xasset_header_stub(game::XAssetType type, const char* name, int create_default)
+ {
+ auto result = try_load_font(name);
+ if (result == nullptr)
+ {
+ result = game::DB_FindXAssetHeader(type, name, create_default).ttf;
+ }
+ return result;
+ }
+ }
+
+ void add(const std::string& name, const std::string& data)
+ {
+ font_data.access([&](font_data_t& data_)
+ {
+ data_.raw_fonts[name] = data;
+ });
+ }
+
+ void clear()
+ {
+ font_data.access([&](font_data_t& data_)
+ {
+ for (auto& font : data_.fonts)
+ {
+ free_font(font.second);
+ }
+
+ data_.fonts.clear();
+ utils::hook::set(0x14EE3ACB8, 0); // reset registered font count
+ });
+ }
+
+ class component final : public component_interface
+ {
+ public:
+ void post_unpack() override
+ {
+ utils::hook::call(0x140747096, db_find_xasset_header_stub);
+ }
+ };
+}
+
+REGISTER_COMPONENT(fonts::component)
diff --git a/src/client/component/fonts.hpp b/src/client/component/fonts.hpp
new file mode 100644
index 00000000..c2d22869
--- /dev/null
+++ b/src/client/component/fonts.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace fonts
+{
+ void add(const std::string& name, const std::string& data);
+ void clear();
+}
diff --git a/src/client/component/fps.cpp b/src/client/component/fps.cpp
index ff522f5d..8233908e 100644
--- a/src/client/component/fps.cpp
+++ b/src/client/component/fps.cpp
@@ -4,7 +4,6 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
-#include "chat.hpp"
#include "scheduler.hpp"
#include "command.hpp"
@@ -303,7 +302,7 @@ namespace fps
{
scheduler::loop(draw, scheduler::pipeline::renderer);
- sub_7C55D0_hook.create(0x7C55D0_b, perf_update);
+ sub_7C55D0_hook.create(0x1407C55D0, perf_update);
cg_drawSpeed = dvars::register_bool("cg_drawSpeed", 0, game::DVAR_FLAG_SAVED);
cg_drawFps = dvars::register_int("cg_drawFPS", 0, 0, 4, game::DVAR_FLAG_SAVED);
diff --git a/src/client/component/gameplay.cpp b/src/client/component/gameplay.cpp
index ce946769..d727335b 100644
--- a/src/client/component/gameplay.cpp
+++ b/src/client/component/gameplay.cpp
@@ -46,10 +46,10 @@ namespace gameplay
a.jnz(allsolid);
a.bind(stand);
- a.jmp(0x6878CD_b);
+ a.jmp(0x1406878CD);
a.bind(allsolid);
- a.jmp(0x6878D4_b);
+ a.jmp(0x1406878D4);
}
void pm_crashland_stub(void* ps, void* pm)
@@ -70,12 +70,12 @@ namespace gameplay
dvars::jump_enableFallDamage = dvars::register_bool("jump_enableFallDamage", true, game::DVAR_FLAG_REPLICATED);
// Influence PM_JitterPoint code flow so the trace->startsolid checks are 'ignored'
- pm_player_trace_hook.create(0x068F0A0_b, &pm_player_trace_stub);
+ pm_player_trace_hook.create(0x14068F0A0, &pm_player_trace_stub);
// If g_enableElevators is 1 the 'ducked' flag will always be removed from the player state
- utils::hook::jump(0x6878C1_b, utils::hook::assemble(pm_trace_stub), true);
+ utils::hook::jump(0x1406878C1, utils::hook::assemble(pm_trace_stub), true);
- pm_crashland_hook.create(0x688A20_b, pm_crashland_stub);
+ pm_crashland_hook.create(0x140688A20, pm_crashland_stub);
dvars::register_float("jump_height", 39, 0, 1000, game::DVAR_FLAG_REPLICATED);
dvars::register_float("g_gravity", 800, 1, 1000, game::DVAR_FLAG_REPLICATED);
diff --git a/src/client/component/gui.cpp b/src/client/component/gui.cpp
index 34e0af53..f8187cbe 100644
--- a/src/client/component/gui.cpp
+++ b/src/client/component/gui.cpp
@@ -208,7 +208,7 @@ namespace gui
a.call_aligned(rbx);
a.mov(ecx, eax);
- a.jmp(0x7A14D1_b);
+ a.jmp(0x1407A14D1);
}
utils::hook::detour wnd_proc_hook;
@@ -301,8 +301,8 @@ namespace gui
void post_unpack() override
{
- utils::hook::jump(0x7A14C4_b, utils::hook::assemble(dxgi_swap_chain_present_stub), true);
- wnd_proc_hook.create(0x650F10_b, wnd_proc_stub);
+ utils::hook::jump(0x1407A14C4, utils::hook::assemble(dxgi_swap_chain_present_stub), true);
+ wnd_proc_hook.create(0x140650F10, wnd_proc_stub);
on_frame([]()
{
@@ -313,8 +313,11 @@ namespace gui
void pre_destroy() override
{
- ImGui_ImplWin32_Shutdown();
- ImGui::DestroyContext();
+ if (initialized)
+ {
+ ImGui_ImplWin32_Shutdown();
+ ImGui::DestroyContext();
+ }
}
};
}
diff --git a/src/client/component/gui_asset_list.cpp b/src/client/component/gui_asset_list.cpp
index b8a02ef3..c4989191 100644
--- a/src/client/component/gui_asset_list.cpp
+++ b/src/client/component/gui_asset_list.cpp
@@ -22,32 +22,31 @@ namespace asset_list
void on_frame()
{
- if (!gui::enabled_menus["asset_list"])
+ static auto* enabled = &gui::enabled_menus["asset_list"];
+ if (!*enabled)
{
return;
}
+ ImGui::Begin("Asset list", enabled);
+
+ ImGui::InputText("asset type", &asset_type_filter);
+ ImGui::BeginChild("asset type list");
+
+ for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
{
- ImGui::Begin("Asset list", &gui::enabled_menus["asset_list"]);
+ const auto name = game::g_assetNames[i];
+ const auto type = static_cast(i);
- ImGui::InputText("asset type", &asset_type_filter);
- ImGui::BeginChild("asset type list");
-
- for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
+ if (utils::string::find_lower(name, asset_type_filter))
{
- const auto name = game::g_assetNames[i];
- const auto type = static_cast(i);
-
- if (utils::string::find_lower(name, asset_type_filter))
- {
- ImGui::Checkbox(name, &shown_assets[type]);
- }
+ ImGui::Checkbox(name, &shown_assets[type]);
}
-
- ImGui::EndChild();
- ImGui::End();
}
+ ImGui::EndChild();
+ ImGui::End();
+
for (auto i = 0; i < game::XAssetType::ASSET_TYPE_COUNT; i++)
{
const auto name = game::g_assetNames[i];
diff --git a/src/client/component/gui_console.cpp b/src/client/component/gui_console.cpp
index 452bf557..13943c7a 100644
--- a/src/client/component/gui_console.cpp
+++ b/src/client/component/gui_console.cpp
@@ -13,7 +13,7 @@
#include
#include
-namespace gui_console
+namespace gui::console
{
namespace
{
@@ -52,7 +52,7 @@ namespace gui_console
}
case ImGuiInputTextFlags_CallbackHistory:
{
- const auto history = game_console::get_history();
+ const auto& history = game_console::get_history();
if (data->EventKey == ImGuiKey_UpArrow)
{
@@ -96,7 +96,7 @@ namespace gui_console
{
std::string text{};
- const auto output = game_console::get_output();
+ const auto& output = game_console::get_output();
for (const auto& line : output)
{
if (utils::string::find_lower(line, filter))
@@ -116,7 +116,8 @@ namespace gui_console
void on_frame()
{
- if (!gui::enabled_menus["console"])
+ static auto* enabled = &gui::enabled_menus["console"];
+ if (!*enabled)
{
return;
}
@@ -126,7 +127,7 @@ namespace gui_console
static const auto input_text_flags = ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_CallbackCompletion |
ImGuiInputTextFlags_CallbackHistory;
- ImGui::Begin("Console", &gui::enabled_menus["console"]);
+ ImGui::Begin("Console", enabled);
if (ImGui::BeginPopup("Options"))
{
@@ -159,7 +160,7 @@ namespace gui_console
ImGui::BeginChild("console_scroll", ImVec2(0, -footer_height_to_reserve), false);
- const auto output = game_console::get_output();
+ const auto& output = game_console::get_output();
for (const auto& line : output)
{
if (utils::string::find_lower(line, filter))
@@ -177,7 +178,7 @@ namespace gui_console
if (ImGui::InputText("Input", &input, input_text_flags, input_text_edit))
{
- auto history = game_console::get_history();
+ auto& history = game_console::get_history();
if (history_index != -1)
{
@@ -209,4 +210,4 @@ namespace gui_console
};
}
-REGISTER_COMPONENT(gui_console::component)
+REGISTER_COMPONENT(gui::console::component)
diff --git a/src/client/component/gui_debug.cpp b/src/client/component/gui_debug.cpp
index 18062b8a..80233264 100644
--- a/src/client/component/gui_debug.cpp
+++ b/src/client/component/gui_debug.cpp
@@ -16,7 +16,7 @@
#include
#include
-namespace gui_debug
+namespace gui::debug
{
namespace
{
@@ -403,12 +403,13 @@ namespace gui_debug
void draw_window()
{
- if (!gui::enabled_menus["debug"])
+ static auto* enabled = &gui::enabled_menus["debug"];
+ if (!*enabled)
{
return;
}
- ImGui::Begin("Debug", &gui::enabled_menus["debug"]);
+ ImGui::Begin("Debug", enabled);
if (ImGui::TreeNode("Path nodes"))
{
@@ -724,4 +725,4 @@ namespace gui_debug
};
}
-REGISTER_COMPONENT(gui_debug::component)
+REGISTER_COMPONENT(gui::debug::component)
diff --git a/src/client/component/gui_entity_list.cpp b/src/client/component/gui_entity_list.cpp
index 40eddb62..cd991e8c 100644
--- a/src/client/component/gui_entity_list.cpp
+++ b/src/client/component/gui_entity_list.cpp
@@ -15,7 +15,7 @@
#include
#include
-namespace entity_list
+namespace gui::entity_list
{
namespace
{
@@ -593,8 +593,9 @@ namespace entity_list
void show_entity_list_window(data_t& data)
{
+ static auto* enabled = &gui::enabled_menus["entity_list"];
ImGui::SetNextWindowSizeConstraints(ImVec2(500, 500), ImVec2(1000, 1000));
- ImGui::Begin("Entity list", &gui::enabled_menus["entity_list"]);
+ ImGui::Begin("Entity list", enabled);
if (ImGui::Button("Update list"))
{
@@ -808,7 +809,8 @@ namespace entity_list
void on_frame()
{
- if (!gui::enabled_menus["entity_list"])
+ static auto* enabled = &gui::enabled_menus["entity_list"];
+ if (!*enabled)
{
return;
}
@@ -858,4 +860,4 @@ namespace entity_list
};
}
-REGISTER_COMPONENT(entity_list::component)
+REGISTER_COMPONENT(gui::entity_list::component)
diff --git a/src/client/component/gui_script_console.cpp b/src/client/component/gui_script_console.cpp
index ca4442ce..4d667de5 100644
--- a/src/client/component/gui_script_console.cpp
+++ b/src/client/component/gui_script_console.cpp
@@ -15,7 +15,7 @@
#include
#include
-namespace gui_script_console
+namespace gui::script_console
{
namespace
{
@@ -228,4 +228,4 @@ namespace gui_script_console
};
}
-REGISTER_COMPONENT(gui_script_console::component)
+REGISTER_COMPONENT(gui::script_console::component)
diff --git a/src/client/component/images.cpp b/src/client/component/images.cpp
index 693f208d..ff7bcf8e 100644
--- a/src/client/component/images.cpp
+++ b/src/client/component/images.cpp
@@ -1,8 +1,11 @@
#include
#include "loader/component_loader.hpp"
-#include "game/game.hpp"
+
#include "images.hpp"
#include "game_console.hpp"
+#include "filesystem.hpp"
+
+#include "game/game.hpp"
#include
#include
@@ -29,7 +32,7 @@ namespace images
}
});
- if (data.empty() && !utils::io::read_file(utils::string::va("images/%s.png", image->name), &data))
+ if (data.empty() && !filesystem::read_file(utils::string::va("images/%s.png", image->name), &data))
{
return {};
}
@@ -111,8 +114,8 @@ namespace images
public:
void post_unpack() override
{
- setup_texture_hook.create(0x74A390_b, setup_texture_stub);
- load_texture_hook.create(0x2A7940_b, load_texture_stub);
+ setup_texture_hook.create(0x14074A390, setup_texture_stub);
+ load_texture_hook.create(0x1402A7940, load_texture_stub);
}
};
}
diff --git a/src/client/component/input.cpp b/src/client/component/input.cpp
index 198ea92a..7b14cc46 100644
--- a/src/client/component/input.cpp
+++ b/src/client/component/input.cpp
@@ -6,6 +6,7 @@
#include "game_console.hpp"
#include "gui.hpp"
#include "game/ui_scripting/lua/engine.hpp"
+#include "game/ui_scripting/execution.hpp"
#include
@@ -25,7 +26,11 @@ namespace input
void cl_char_event_stub(const int local_client_num, const int key)
{
- ui_scripting::lua::engine::ui_event("char", {key});
+ ui_scripting::notify("keypress",
+ {
+ {"keynum", key},
+ {"key", game::Key_KeynumToString(key, 0, 1)},
+ });
if (!game_console::console_char_event(local_client_num, key))
{
@@ -42,7 +47,11 @@ namespace input
void cl_key_event_stub(const int local_client_num, const int key, const int down)
{
- ui_scripting::lua::engine::ui_event("key", {key, down});
+ ui_scripting::notify(down ? "keydown" : "keyup",
+ {
+ {"keynum", key},
+ {"key", game::Key_KeynumToString(key, 0, 1)},
+ });
if (!game_console::console_key_event(local_client_num, key, down))
{
@@ -64,7 +73,6 @@ namespace input
return;
}
- ui_scripting::lua::engine::ui_event("mousemove", {x, y});
cl_mouse_move_hook.invoke(local_client_num, x, y);
}
}
@@ -74,9 +82,9 @@ namespace input
public:
void post_unpack() override
{
- cl_char_event_hook.create(0x3D27B0_b, cl_char_event_stub);
- cl_key_event_hook.create(0x3D2AE0_b, cl_key_event_stub);
- cl_mouse_move_hook.create(0x3296F0_b, cl_mouse_move_stub);
+ cl_char_event_hook.create(0x1403D27B0, cl_char_event_stub);
+ cl_key_event_hook.create(0x1403D2AE0, cl_key_event_stub);
+ cl_mouse_move_hook.create(0x1403296F0, cl_mouse_move_stub);
}
};
}
diff --git a/src/client/component/localized_strings.cpp b/src/client/component/localized_strings.cpp
index f09fe247..b968d498 100644
--- a/src/client/component/localized_strings.cpp
+++ b/src/client/component/localized_strings.cpp
@@ -46,7 +46,7 @@ namespace localized_strings
void post_unpack() override
{
// Change some localized strings
- seh_string_ed_get_string_hook.create(0x5E5FD0_b, &seh_string_ed_get_string);
+ seh_string_ed_get_string_hook.create(0x1405E5FD0, &seh_string_ed_get_string);
}
};
}
diff --git a/src/client/component/logger.cpp b/src/client/component/logger.cpp
index 9d7b4b50..554ce013 100644
--- a/src/client/component/logger.cpp
+++ b/src/client/component/logger.cpp
@@ -142,10 +142,10 @@ namespace logger
public:
void post_unpack() override
{
- utils::hook::jump(0x32C620_b, print_warning, true);
- utils::hook::jump(0x32C630_b, print_warning, true);
- utils::hook::jump(0x32AEF0_b, lui_print, true);
- com_error_hook.create(0x5A2D80_b, com_error_stub);
+ utils::hook::jump(0x14032C620, print_warning, true);
+ utils::hook::jump(0x14032C630, print_warning, true);
+ utils::hook::jump(0x14032AEF0, lui_print, true);
+ com_error_hook.create(0x1405A2D80, com_error_stub);
}
};
}
diff --git a/src/client/component/lui.cpp b/src/client/component/lui.cpp
index 87ee13b4..a1d9988d 100644
--- a/src/client/component/lui.cpp
+++ b/src/client/component/lui.cpp
@@ -39,8 +39,8 @@ namespace lui
command::add("lui_restart", []()
{
- utils::hook::invoke(0x3203B0_b);
- utils::hook::invoke(0x32D370_b);
+ utils::hook::invoke(0x1403203B0);
+ utils::hook::invoke(0x14032D370);
});
}
};
diff --git a/src/client/component/materials.cpp b/src/client/component/materials.cpp
new file mode 100644
index 00000000..d84a6b93
--- /dev/null
+++ b/src/client/component/materials.cpp
@@ -0,0 +1,200 @@
+#include
+#include "loader/component_loader.hpp"
+
+#include "materials.hpp"
+#include "game_console.hpp"
+#include "filesystem.hpp"
+
+#include "game/game.hpp"
+#include "game/dvars.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+namespace materials
+{
+ namespace
+ {
+ utils::hook::detour db_material_streaming_fail_hook;
+ utils::hook::detour material_register_handle_hook;
+ utils::hook::detour db_get_material_index_hook;
+
+ struct material_data_t
+ {
+ std::unordered_map materials;
+ std::unordered_map images;
+ };
+
+ char constant_table[0x20] = {};
+
+ utils::concurrency::container material_data;
+
+ game::GfxImage* setup_image(game::GfxImage* image, const utils::image& raw_image)
+ {
+ image->imageFormat = 0x1000003;
+ image->resourceSize = -1;
+
+ D3D11_SUBRESOURCE_DATA data{};
+ data.SysMemPitch = raw_image.get_width() * 4;
+ data.SysMemSlicePitch = data.SysMemPitch * raw_image.get_height();
+ data.pSysMem = raw_image.get_buffer();
+
+ game::Image_Setup(image, raw_image.get_width(), raw_image.get_height(), image->depth, image->numElements,
+ image->imageFormat, DXGI_FORMAT_R8G8B8A8_UNORM, 0, image->name, &data);
+
+ return image;
+ }
+
+ game::Material* create_material(const std::string& name, const std::string& data)
+ {
+ const auto white = *reinterpret_cast(0x141B09208);
+
+ const auto material = utils::memory::get_allocator()->allocate();
+ const auto texture_table = utils::memory::get_allocator()->allocate();
+ const auto image = utils::memory::get_allocator()->allocate();
+
+ std::memcpy(material, white, sizeof(game::Material));
+ std::memcpy(texture_table, white->textureTable, sizeof(game::MaterialTextureDef));
+ std::memcpy(image, white->textureTable->u.image, sizeof(game::GfxImage));
+
+ material->constantTable = &constant_table;
+ material->name = utils::memory::get_allocator()->duplicate_string(name);
+ image->name = material->name;
+
+ material->textureTable = texture_table;
+ material->textureTable->u.image = setup_image(image, data);
+
+ return material;
+ }
+
+ void free_material(game::Material* material)
+ {
+ material->textureTable->u.image->textures.___u0.map->Release();
+ material->textureTable->u.image->textures.shaderView->Release();
+ utils::memory::get_allocator()->free(material->textureTable->u.image);
+ utils::memory::get_allocator()->free(material->textureTable);
+ utils::memory::get_allocator()->free(material->name);
+ utils::memory::get_allocator()->free(material);
+ }
+
+ game::Material* load_material(const std::string& name)
+ {
+ return material_data.access([&](material_data_t& data_) -> game::Material*
+ {
+ if (const auto i = data_.materials.find(name); i != data_.materials.end())
+ {
+ return i->second;
+ }
+
+ std::string data{};
+ if (const auto i = data_.images.find(name); i != data_.images.end())
+ {
+ data = i->second;
+ }
+
+ if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
+ {
+ data_.materials[name] = nullptr;
+ return nullptr;
+ }
+
+ const auto material = create_material(name, data);
+ data_.materials[name] = material;
+
+ return material;
+ });
+ }
+
+ game::Material* try_load_material(const std::string& name)
+ {
+ if (name == "white")
+ {
+ return nullptr;
+ }
+
+ try
+ {
+ return load_material(name);
+ }
+ catch (const std::exception& e)
+ {
+ game_console::print(game_console::con_type_error, "Failed to load material %s: %s\n", name.data(), e.what());
+ }
+
+ return nullptr;
+ }
+
+ game::Material* material_register_handle_stub(const char* name)
+ {
+ auto result = try_load_material(name);
+ if (result == nullptr)
+ {
+ result = material_register_handle_hook.invoke(name);
+ }
+ return result;
+ }
+
+ int db_material_streaming_fail_stub(game::Material* material)
+ {
+ if (material->constantTable == &constant_table)
+ {
+ return 0;
+ }
+
+ return db_material_streaming_fail_hook.invoke(material);
+ }
+
+ unsigned int db_get_material_index_stub(game::Material* material)
+ {
+ if (material->constantTable == &constant_table)
+ {
+ return 0;
+ }
+
+ return db_get_material_index_hook.invoke(material);
+ }
+ }
+
+ void add(const std::string& name, const std::string& data)
+ {
+ material_data.access([&](material_data_t& data_)
+ {
+ data_.images[name] = data;
+ });
+ }
+
+ void clear()
+ {
+ material_data.access([&](material_data_t& data_)
+ {
+ for (auto& material : data_.materials)
+ {
+ if (material.second == nullptr)
+ {
+ continue;
+ }
+
+ free_material(material.second);
+ }
+
+ data_.materials.clear();
+ });
+ }
+
+ class component final : public component_interface
+ {
+ public:
+ void post_unpack() override
+ {
+ material_register_handle_hook.create(game::Material_RegisterHandle.get(), material_register_handle_stub);
+ db_material_streaming_fail_hook.create(0x14041D140, db_material_streaming_fail_stub);
+ db_get_material_index_hook.create(0x140413BC0, db_get_material_index_stub);
+ }
+ };
+}
+
+REGISTER_COMPONENT(materials::component)
diff --git a/src/client/component/materials.hpp b/src/client/component/materials.hpp
new file mode 100644
index 00000000..3a548548
--- /dev/null
+++ b/src/client/component/materials.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+namespace materials
+{
+ void add(const std::string& name, const std::string& data);
+ void clear();
+}
diff --git a/src/client/component/mods.cpp b/src/client/component/mods.cpp
index 32b58052..dd5404bb 100644
--- a/src/client/component/mods.cpp
+++ b/src/client/component/mods.cpp
@@ -6,12 +6,45 @@
#include "command.hpp"
#include "game_console.hpp"
#include "scheduler.hpp"
+#include "filesystem.hpp"
+#include "materials.hpp"
+#include "fonts.hpp"
+#include "mods.hpp"
#include
#include
namespace mods
{
+ std::string mod_path{};
+
+ namespace
+ {
+ utils::hook::detour db_release_xassets_hook;
+ bool release_assets = false;
+
+ void db_release_xassets_stub()
+ {
+ if (release_assets)
+ {
+ materials::clear();
+ fonts::clear();
+ }
+
+ db_release_xassets_hook.invoke();
+ }
+
+ void restart()
+ {
+ scheduler::once([]()
+ {
+ release_assets = true;
+ game::Com_Shutdown("");
+ release_assets = false;
+ }, scheduler::pipeline::main);
+ }
+ }
+
class component final : public component_interface
{
public:
@@ -22,6 +55,8 @@ namespace mods
utils::io::create_directory("mods");
}
+ db_release_xassets_hook.create(0x140416A80, db_release_xassets_stub);
+
command::add("loadmod", [](const command::params& params)
{
if (params.size() < 2)
@@ -30,7 +65,7 @@ namespace mods
return;
}
- if (::game::SV_Loaded())
+ if (!game::Com_InFrontend())
{
game_console::print(game_console::con_type_error, "Cannot load mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
@@ -38,44 +73,38 @@ namespace mods
}
const auto path = params.get(1);
- game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
-
if (!utils::io::directory_exists(path))
{
game_console::print(game_console::con_type_error, "Mod %s not found!\n", path);
return;
}
- game::mod_folder = path;
-
- scheduler::once([]()
- {
- command::execute("lui_restart", true);
- }, scheduler::pipeline::renderer);
+ game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
+ filesystem::get_search_paths().erase(mod_path);
+ filesystem::get_search_paths().insert(path);
+ mod_path = path;
+ restart();
});
command::add("unloadmod", [](const command::params& params)
{
- if (game::mod_folder.empty())
+ if (mod_path.empty())
{
game_console::print(game_console::con_type_info, "No mod loaded\n");
return;
}
- if (::game::SV_Loaded())
+ if (!game::Com_InFrontend())
{
game_console::print(game_console::con_type_error, "Cannot unload mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
return;
}
- game_console::print(game_console::con_type_info, "Unloading mod %s\n", game::mod_folder.data());
- game::mod_folder.clear();
-
- scheduler::once([]()
- {
- command::execute("lui_restart", true);
- }, scheduler::pipeline::renderer);
+ game_console::print(game_console::con_type_info, "Unloading mod %s\n", mod_path.data());
+ filesystem::get_search_paths().erase(mod_path);
+ mod_path.clear();
+ restart();
});
}
};
diff --git a/src/client/component/mods.hpp b/src/client/component/mods.hpp
new file mode 100644
index 00000000..364a2f11
--- /dev/null
+++ b/src/client/component/mods.hpp
@@ -0,0 +1,6 @@
+#pragma once
+
+namespace mods
+{
+ extern std::string mod_path;
+}
\ No newline at end of file
diff --git a/src/client/component/notifies.cpp b/src/client/component/notifies.cpp
index 1419fa67..d2ecb7f3 100644
--- a/src/client/component/notifies.cpp
+++ b/src/client/component/notifies.cpp
@@ -32,20 +32,20 @@ namespace notifies
{
if (vm_execute_hooks.find(pos) == vm_execute_hooks.end())
{
+ hook_enabled = true;
return false;
}
- if (!hook_enabled && pos > (char*)vm_execute_hooks.size())
+ if (!hook_enabled && pos > reinterpret_cast(vm_execute_hooks.size()))
{
hook_enabled = true;
return false;
}
- const auto hook = vm_execute_hooks[pos];
+ const auto& hook = vm_execute_hooks[pos];
const auto state = hook.lua_state();
- const auto self_id = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
- const auto self = scripting::entity(self_id);
+ const scripting::entity self = local_id_to_entity(game::scr_VmPub->function_frame->fs.localId);
std::vector args;
@@ -59,16 +59,6 @@ namespace notifies
const auto result = hook(self, sol::as_args(args));
scripting::lua::handle_error(result);
- const auto value = scripting::lua::convert({state, result});
- const auto type = value.get_raw().type;
-
- game::Scr_ClearOutParams();
-
- if (result.valid() && type && type < game::SCRIPT_END)
- {
- scripting::push_value(value);
- }
-
return true;
}
@@ -94,12 +84,12 @@ namespace notifies
a.inc(r14);
a.mov(dword_ptr(rbp, 0xA4), r15d);
- a.jmp(0x5C90B3_b);
+ a.jmp(0x1405C90B3);
a.bind(replace);
a.popad64();
- a.mov(r14, (char*)empty_function);
+ a.mov(r14, reinterpret_cast(empty_function));
a.jmp(end);
}
@@ -137,7 +127,7 @@ namespace notifies
std::string convert_mod(const int meansOfDeath)
{
- const auto value = reinterpret_cast(0xBF49B0_b)[meansOfDeath];
+ const auto value = reinterpret_cast(0x140BF49B0)[meansOfDeath];
const auto string = game::SL_ConvertToString(*value);
return string;
@@ -147,7 +137,7 @@ namespace notifies
int damage, int dflags, const unsigned int hitLoc, const unsigned int weapon, bool isAlternate, unsigned int a11, const int meansOfDeath, unsigned int a13, unsigned int a14)
{
{
- const std::string _hitLoc = reinterpret_cast(0xBF4AA0_b)[hitLoc];
+ const std::string _hitLoc = reinterpret_cast(0x140BF4AA0)[hitLoc];
const auto _mod = convert_mod(meansOfDeath);
const auto _weapon = get_weapon_name(weapon, isAlternate);
@@ -199,9 +189,9 @@ namespace notifies
public:
void post_unpack() override
{
- utils::hook::jump(0x5C90A5_b, utils::hook::assemble(vm_execute_stub), true);
+ utils::hook::jump(0x1405C90A5, utils::hook::assemble(vm_execute_stub), true);
- scr_entity_damage_hook.create(0x4BD2E0_b, scr_entity_damage_stub);
+ scr_entity_damage_hook.create(0x1404BD2E0, scr_entity_damage_stub);
}
};
}
diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp
index 8465f83d..c569b84a 100644
--- a/src/client/component/patches.cpp
+++ b/src/client/component/patches.cpp
@@ -16,7 +16,7 @@ namespace patches
void* sub_46148()
{
- static uint64_t off_11C52460 = 0xAD0C58_b;
+ static uint64_t off_11C52460 = 0x140AD0C58;
return &off_11C52460;
}
@@ -28,7 +28,7 @@ namespace patches
void gscr_set_save_dvar_stub()
{
- const auto string = utils::string::to_lower(utils::hook::invoke(0x5C7C20_b, 0));
+ const auto string = utils::string::to_lower(utils::hook::invoke(0x1405C7C20, 0));
if (string == "cg_fov" || string == "cg_fovscale")
{
return;
@@ -65,24 +65,24 @@ namespace patches
void post_unpack() override
{
// Fix startup crashes
- utils::hook::set(0x633080_b, 0xC301B0);
- utils::hook::set(0x272F70_b, 0xC301B0);
- utils::hook::jump(0x46148_b, sub_46148, true);
+ utils::hook::set(0x140633080, 0xC301B0);
+ utils::hook::set(0x140272F70, 0xC301B0);
+ utils::hook::jump(0x140046148, sub_46148, true);
- utils::hook::jump(0x64EF10_b, quit_stub, true);
+ utils::hook::jump(0x14064EF10, quit_stub, true);
// Unlock fps in main menu
- utils::hook::set(0x3D8E1B_b, 0xEB);
+ utils::hook::set(0x1403D8E1B, 0xEB);
// Disable battle net popup
- utils::hook::nop(0x5F4496_b, 5);
+ utils::hook::nop(0x1405F4496, 5);
// Allow kbam input when gamepad is enabled
- utils::hook::nop(0x3D2F8E_b, 2);
- utils::hook::nop(0x3D0C9C_b, 6);
+ utils::hook::nop(0x1403D2F8E, 2);
+ utils::hook::nop(0x1403D0C9C, 6);
// Prevent game from overriding cg_fov and cg_fovscale values
- gscr_set_save_dvar_hook.create(0x504C60_b, &gscr_set_save_dvar_stub);
+ gscr_set_save_dvar_hook.create(0x140504C60, &gscr_set_save_dvar_stub);
// Make cg_fov and cg_fovscale saved dvars
diff --git a/src/client/component/renderer.cpp b/src/client/component/renderer.cpp
index be9f7360..236f8a45 100644
--- a/src/client/component/renderer.cpp
+++ b/src/client/component/renderer.cpp
@@ -63,8 +63,8 @@ namespace renderer
{
dvars::r_fullbright = dvars::register_int("r_fullbright", 0, 0, 3, game::DVAR_FLAG_SAVED);
- r_init_draw_method_hook.create(0x72F950_b, &r_init_draw_method_stub);
- r_update_front_end_dvar_options_hook.create(0x76EE70_b, &r_update_front_end_dvar_options_stub);
+ r_init_draw_method_hook.create(0x14072F950, &r_init_draw_method_stub);
+ r_update_front_end_dvar_options_hook.create(0x14076EE70, &r_update_front_end_dvar_options_stub);
}
};
}
diff --git a/src/client/component/scheduler.cpp b/src/client/component/scheduler.cpp
index a266a693..ed0301b8 100644
--- a/src/client/component/scheduler.cpp
+++ b/src/client/component/scheduler.cpp
@@ -183,9 +183,9 @@ namespace scheduler
void post_unpack() override
{
- r_end_frame_hook.create(0x76D7B0_b, scheduler::r_end_frame_stub);
- g_run_frame_hook.create(0x4CB030_b, scheduler::server_frame_stub);
- main_frame_hook.create(0x417FA0_b, scheduler::main_frame_stub);
+ r_end_frame_hook.create(0x14076D7B0, scheduler::r_end_frame_stub);
+ g_run_frame_hook.create(0x1404CB030, scheduler::server_frame_stub);
+ main_frame_hook.create(0x140417FA0, scheduler::main_frame_stub);
}
void pre_destroy() override
diff --git a/src/client/component/scripting.cpp b/src/client/component/scripting.cpp
index cf4167cd..491341d6 100644
--- a/src/client/component/scripting.cpp
+++ b/src/client/component/scripting.cpp
@@ -25,7 +25,8 @@ namespace scripting
utils::hook::detour vm_notify_hook;
utils::hook::detour g_shutdown_game_hook;
- utils::hook::detour player_spawn_hook;
+ utils::hook::detour client_spawn_hook;
+ utils::hook::detour sv_check_load_level_hook;
utils::hook::detour scr_add_class_field_hook;
@@ -55,9 +56,9 @@ namespace scripting
vm_notify_hook.invoke(notify_list_owner_id, string_value, top);
}
- void player_spawn_stub(const game::gentity_s* player)
+ void client_spawn_stub(const game::gentity_s* client)
{
- player_spawn_hook.invoke(player);
+ client_spawn_hook.invoke(client);
lua::engine::start();
}
@@ -67,16 +68,16 @@ namespace scripting
g_shutdown_game_hook.invoke(free_scripts);
}
- void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t _name, unsigned int canonicalString, unsigned int offset)
+ void scr_add_class_field_stub(unsigned int classnum, game::scr_string_t name, unsigned int canonicalString, unsigned int offset)
{
- const auto name = game::SL_ConvertToString(_name);
+ const auto name_ = game::SL_ConvertToString(name);
- if (fields_table[classnum].find(name) == fields_table[classnum].end())
+ if (fields_table[classnum].find(name_) == fields_table[classnum].end())
{
- fields_table[classnum][name] = offset;
+ fields_table[classnum][name_] = offset;
}
- scr_add_class_field_hook.invoke(classnum, _name, canonicalString, offset);
+ scr_add_class_field_hook.invoke(classnum, name, canonicalString, offset);
}
void process_script_stub(const char* filename)
@@ -99,11 +100,10 @@ namespace scripting
scr_set_thread_position_hook.invoke(threadName, codePos);
}
- utils::hook::detour sub_6B2940_hook;
- char sub_6B2940_stub(void* a1)
+ char sv_check_load_level_stub(void* save_game)
{
- const auto result = sub_6B2940_hook.invoke(a1);
- if (a1 != nullptr)
+ const auto result = sv_check_load_level_hook.invoke(save_game);
+ if (save_game != nullptr)
{
lua::engine::start();
}
@@ -116,19 +116,15 @@ namespace scripting
public:
void post_unpack() override
{
- vm_notify_hook.create(0x5CC450_b, vm_notify_stub);
+ vm_notify_hook.create(0x1405CC450, vm_notify_stub);
- g_shutdown_game_hook.create(0x4CBAD0_b, g_shutdown_game_stub);
- player_spawn_hook.create(0x4B0710_b, player_spawn_stub);
+ g_shutdown_game_hook.create(0x1404CBAD0, g_shutdown_game_stub);
+ client_spawn_hook.create(0x1404B0710, client_spawn_stub);
+ sv_check_load_level_hook.create(0x1406B2940, sv_check_load_level_stub);
- scr_add_class_field_hook.create(0x5C2C30_b, scr_add_class_field_stub);
- scr_set_thread_position_hook.create(0x5BC7E0_b, scr_set_thread_position_stub);
- process_script_hook.create(0x5C6160_b, process_script_stub);
-
- // Loading last checkpoint doesn't call spawn player again (player_spawn_hook)
- // Not sure what this function does but `a1` is != nullptr when loading
- // the last checkpoint so we need to start lua in this context
- sub_6B2940_hook.create(0x6B2940_b, sub_6B2940_stub);
+ scr_add_class_field_hook.create(0x1405C2C30, scr_add_class_field_stub);
+ scr_set_thread_position_hook.create(0x1405BC7E0, scr_set_thread_position_stub);
+ process_script_hook.create(0x1405C6160, process_script_stub);
scheduler::loop([]()
{
diff --git a/src/client/component/ui_scripting.cpp b/src/client/component/ui_scripting.cpp
index a810606c..ee6d4831 100644
--- a/src/client/component/ui_scripting.cpp
+++ b/src/client/component/ui_scripting.cpp
@@ -4,7 +4,6 @@
#include "game/game.hpp"
#include "game/dvars.hpp"
-#include "chat.hpp"
#include "scheduler.hpp"
#include "command.hpp"
@@ -27,8 +26,12 @@ namespace ui_scripting
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 lui_error_hook;
+ utils::hook::detour hksi_hks_error_hook;
+ utils::hook::detour hks_frame_hook;
- bool error_hook_enabled = false;
+ int error_hook_enabled = 0;
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
{
@@ -45,10 +48,37 @@ namespace ui_scripting
{
return hksi_lual_error_hook.invoke(s, formatted.data());
}
- else
+
+ throw std::runtime_error(formatted);
+ }
+
+ void hksi_hks_error_stub(game::hks::lua_State* s, int a2)
+ {
+ if (!error_hook_enabled)
{
- throw std::runtime_error(formatted);
+ return hksi_hks_error_hook.invoke(s, a2);
}
+
+ throw std::runtime_error("unknown error");
+ }
+
+ void lui_error_stub(game::hks::lua_State* s)
+ {
+ if (!error_hook_enabled)
+ {
+ return lui_error_hook.invoke(s);
+ }
+
+ const auto count = static_cast(s->m_apistack.top - s->m_apistack.base);
+ const auto arguments = get_return_values(count);
+
+ std::string error_str = "LUI Error";
+ if (count && arguments[0].is())
+ {
+ error_str = arguments[0].as();
+ }
+
+ throw std::runtime_error(error_str);
}
void* hks_start_stub(char a1)
@@ -66,6 +96,26 @@ namespace ui_scripting
ui_scripting::lua::engine::stop();
hks_shutdown_hook.invoke();
}
+
+ void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
+ {
+ const auto closure = reinterpret_cast(oldMemory);
+ if (converted_functions.find(closure) != converted_functions.end())
+ {
+ converted_functions.erase(closure);
+ }
+
+ return hks_allocator_hook.invoke(userData, oldMemory, oldSize, newSize);
+ }
+
+ void hks_frame_stub()
+ {
+ const auto state = *game::hks::lua_state;
+ if (state)
+ {
+ ui_scripting::lua::engine::run_frame();
+ }
+ }
}
int main_function_handler(game::hks::lua_State* state)
@@ -117,17 +167,12 @@ namespace ui_scripting
void enable_error_hook()
{
- error_hook_enabled = true;
+ error_hook_enabled++;
}
void disable_error_hook()
{
- error_hook_enabled = false;
- }
-
- void notify(const event& e)
- {
- lua::engine::notify(e);
+ error_hook_enabled--;
}
class component final : public component_interface
@@ -136,12 +181,14 @@ namespace ui_scripting
void post_unpack() override
{
- scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui);
-
- hks_start_hook.create(0x328BE0_b, hks_start_stub);
- hks_shutdown_hook.create(0x3203B0_b, hks_shutdown_stub);
- hksi_lual_error_hook.create(0x2E3E40_b, hksi_lual_error_stub);
- hksi_lual_error_hook2.create(0x2DCB40_b, hksi_lual_error_stub);
+ hks_frame_hook.create(0x140327880, hks_frame_stub);
+ hks_start_hook.create(0x140328BE0, hks_start_stub);
+ hks_shutdown_hook.create(0x1403203B0, hks_shutdown_stub);
+ hksi_lual_error_hook.create(0x1402E3E40, hksi_lual_error_stub);
+ hksi_lual_error_hook2.create(0x1402DCB40, hksi_lual_error_stub);
+ hks_allocator_hook.create(0x1402D92A0, hks_allocator_stub);
+ lui_error_hook.create(0x1402B9D90, lui_error_stub);
+ hksi_hks_error_hook.create(0x1402DBC00, hksi_hks_error_stub);
}
};
}
diff --git a/src/client/component/ui_scripting.hpp b/src/client/component/ui_scripting.hpp
index 8831d39d..417619fe 100644
--- a/src/client/component/ui_scripting.hpp
+++ b/src/client/component/ui_scripting.hpp
@@ -10,6 +10,4 @@ namespace ui_scripting
void enable_error_hook();
void disable_error_hook();
-
- void notify(const event& e);
}
diff --git a/src/client/component/updater.cpp b/src/client/component/updater.cpp
new file mode 100644
index 00000000..2bb6e19e
--- /dev/null
+++ b/src/client/component/updater.cpp
@@ -0,0 +1,408 @@
+#include
+#include "loader/component_loader.hpp"
+
+#include "scheduler.hpp"
+#include "updater.hpp"
+
+#include "version.h"
+
+#include "game/game.hpp"
+#include "game/dvars.hpp"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MASTER "https://master.fed0001.xyz/"
+
+#define FILES_PATH "files.json"
+#define FILES_PATH_DEV "files-dev.json"
+
+#define DATA_PATH "data/"
+#define DATA_PATH_DEV "data-dev/"
+
+#define ERR_UPDATE_CHECK_FAIL "Failed to check for updates"
+#define ERR_DOWNLOAD_FAIL "Failed to download file "
+#define ERR_WRITE_FAIL "Failed to write file "
+
+namespace updater
+{
+ namespace
+ {
+ game::dvar_t* cl_auto_update;
+ bool has_tried_update = false;
+
+ struct status
+ {
+ bool done;
+ bool success;
+ };
+
+ struct file_data
+ {
+ std::string name;
+ std::string data;
+ };
+
+ struct update_data_t
+ {
+ bool restart_required{};
+ bool cancelled{};
+ status check{};
+ status download{};
+ std::string error{};
+ std::string current_file{};
+ std::vector required_files{};
+ };
+
+ utils::concurrency::container update_data;
+
+ std::string select(const std::string& main, const std::string& develop)
+ {
+ if (GIT_BRANCH == "develop"s)
+ {
+ return develop;
+ }
+
+ return main;
+ }
+
+ void set_update_check_status(bool done, bool success, const std::string& error = {})
+ {
+ update_data.access([done, success, error](update_data_t& data_)
+ {
+ data_.check.done = done;
+ data_.check.success = success;
+ data_.error = error;
+ });
+ }
+
+ void set_update_download_status(bool done, bool success, const std::string& error = {})
+ {
+ update_data.access([done, success, error](update_data_t& data_)
+ {
+ data_.download.done = done;
+ data_.download.success = success;
+ data_.error = error;
+ });
+ }
+
+ bool check_file(const std::string& name, const std::string& sha)
+ {
+ std::string data;
+ if (!utils::io::read_file(name, &data))
+ {
+ return false;
+ }
+
+ if (utils::cryptography::sha1::compute(data, true) != sha)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ std::string load_binary_name()
+ {
+ utils::nt::library self;
+ return self.get_name();
+ }
+
+ std::string get_binary_name()
+ {
+ static const auto name = load_binary_name();
+ return name;
+ }
+
+ std::string get_time_str()
+ {
+ return utils::string::va("%i", uint32_t(time(nullptr)));
+ }
+
+ std::optional download_file(const std::string& name)
+ {
+ return utils::http::get_data(MASTER + select(DATA_PATH, DATA_PATH_DEV) + name + "?" + get_time_str());
+ }
+
+ bool is_update_cancelled()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.cancelled;
+ });
+ }
+
+ bool write_file(const std::string& name, const std::string& data)
+ {
+ if (get_binary_name() == name &&
+ utils::io::file_exists(name) &&
+ !utils::io::move_file(name, name + ".old"))
+ {
+ return false;
+ }
+
+ return utils::io::write_file(name, data);
+ }
+
+ void delete_old_file()
+ {
+ utils::io::remove_file(get_binary_name() + ".old");
+ }
+
+ void reset_data()
+ {
+ update_data.access([](update_data_t& data_)
+ {
+ data_ = {};
+ });
+ }
+ }
+
+ void relaunch()
+ {
+ utils::nt::relaunch_self("-singleplayer");
+ utils::nt::terminate();
+ }
+
+ void set_has_tried_update(bool tried)
+ {
+ has_tried_update = tried;
+ }
+
+ bool get_has_tried_update()
+ {
+ return has_tried_update;
+ }
+
+ bool auto_updates_enabled()
+ {
+ return cl_auto_update->current.enabled;
+ }
+
+ bool is_update_check_done()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.check.done;
+ });
+ }
+
+ bool is_update_download_done()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.download.done;
+ });
+ }
+
+ bool get_update_check_status()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.check.success;
+ });
+ }
+
+ bool get_update_download_status()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.download.success;
+ });
+ }
+
+ bool is_update_available()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.required_files.size() > 0;
+ });
+ }
+
+ bool is_restart_required()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.restart_required;
+ });
+ }
+
+ std::string get_last_error()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.error;
+ });
+ }
+
+ std::string get_current_file()
+ {
+ return update_data.access([](update_data_t& data_)
+ {
+ return data_.current_file;
+ });
+ }
+
+ void cancel_update()
+ {
+#ifdef DEBUG
+ printf("[Updater] Cancelling update\n");
+#endif
+
+ return update_data.access([](update_data_t& data_)
+ {
+ data_.cancelled = true;
+ });
+ }
+
+ void start_update_check()
+ {
+ cancel_update();
+ reset_data();
+
+#ifdef DEBUG
+ printf("[Updater] starting update check\n");
+#endif
+
+ scheduler::once([]()
+ {
+ const auto files_data = utils::http::get_data(MASTER + select(FILES_PATH, FILES_PATH_DEV) + "?" + get_time_str());
+
+ if (is_update_cancelled())
+ {
+ reset_data();
+ return;
+ }
+
+ if (!files_data.has_value())
+ {
+ set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
+ return;
+ }
+
+ rapidjson::Document j;
+ j.Parse(files_data.value().data());
+
+ if (!j.IsArray())
+ {
+ set_update_check_status(true, false, ERR_UPDATE_CHECK_FAIL);
+ return;
+ }
+
+ std::vector required_files;
+
+ const auto files = j.GetArray();
+ for (const auto& file : files)
+ {
+ if (!file.IsArray() || file.Size() != 3 || !file[0].IsString() || !file[2].IsString())
+ {
+ continue;
+ }
+
+ const auto name = file[0].GetString();
+ const auto sha = file[2].GetString();
+
+ if (!check_file(name, sha))
+ {
+ if (get_binary_name() == name)
+ {
+ update_data.access([](update_data_t& data_)
+ {
+ data_.restart_required = true;
+ });
+ }
+
+#ifdef DEBUG
+ printf("[Updater] need file %s\n", name);
+#endif
+
+ required_files.push_back(name);
+ }
+ }
+
+ update_data.access([&required_files](update_data_t& data_)
+ {
+ data_.check.done = true;
+ data_.check.success = true;
+ data_.required_files = required_files;
+ });
+ }, scheduler::pipeline::async);
+ }
+
+ void start_update_download()
+ {
+#ifdef DEBUG
+ printf("[Updater] starting update download\n");
+#endif
+
+ if (!is_update_check_done() || !get_update_check_status() || is_update_cancelled())
+ {
+ return;
+ }
+
+ scheduler::once([]()
+ {
+ const auto required_files = update_data.access>([](update_data_t& data_)
+ {
+ return data_.required_files;
+ });
+
+ std::vector downloads;
+
+ for (const auto& file : required_files)
+ {
+ update_data.access([file](update_data_t& data_)
+ {
+ data_.current_file = file;
+ });
+
+#ifdef DEBUG
+ printf("[Updater] downloading file %s\n", file.data());
+#endif
+
+ const auto data = download_file(file);
+
+ if (is_update_cancelled())
+ {
+ reset_data();
+ return;
+ }
+
+ if (!data.has_value())
+ {
+ set_update_download_status(true, false, ERR_DOWNLOAD_FAIL + file);
+ return;
+ }
+
+ downloads.push_back({file, data.value()});
+ }
+
+ for (const auto& download : downloads)
+ {
+ if (!write_file(download.name, download.data))
+ {
+ set_update_download_status(true, false, ERR_WRITE_FAIL + download.name);
+ return;
+ }
+ }
+
+ set_update_download_status(true, true);
+ }, scheduler::pipeline::async);
+ }
+
+ class component final : public component_interface
+ {
+ public:
+ void post_unpack() override
+ {
+ delete_old_file();
+ cl_auto_update = dvars::register_bool("cg_auto_update", true, game::DVAR_FLAG_SAVED);
+ }
+ };
+}
+
+REGISTER_COMPONENT(updater::component)
diff --git a/src/client/component/updater.hpp b/src/client/component/updater.hpp
new file mode 100644
index 00000000..9a3dd45e
--- /dev/null
+++ b/src/client/component/updater.hpp
@@ -0,0 +1,26 @@
+#pragma once
+
+namespace updater
+{
+ void relaunch();
+
+ void set_has_tried_update(bool tried);
+ bool get_has_tried_update();
+ bool auto_updates_enabled();
+
+ bool is_update_available();
+ bool is_update_check_done();
+ bool get_update_check_status();
+
+ bool is_update_download_done();
+ bool get_update_download_status();
+
+ bool is_restart_required();
+
+ std::string get_last_error();
+ std::string get_current_file();
+
+ void start_update_check();
+ void start_update_download();
+ void cancel_update();
+}
\ No newline at end of file
diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp
index 28261c72..b94b6a23 100644
--- a/src/client/game/game.cpp
+++ b/src/client/game/game.cpp
@@ -3,16 +3,6 @@
namespace game
{
- uint64_t base_address;
-
- void load_base_address()
- {
- const auto module = GetModuleHandle(NULL);
- base_address = uint64_t(module);
- }
-
- std::string mod_folder{};
-
namespace environment
{
launcher::mode mode = launcher::mode::none;
@@ -81,8 +71,3 @@ namespace game
}
}
}
-
-uintptr_t operator"" _b(const uintptr_t ptr)
-{
- return game::base_address + ptr;
-}
\ No newline at end of file
diff --git a/src/client/game/game.hpp b/src/client/game/game.hpp
index 601179ad..cad966e5 100644
--- a/src/client/game/game.hpp
+++ b/src/client/game/game.hpp
@@ -5,11 +5,6 @@
namespace game
{
- extern uint64_t base_address;
- void load_base_address();
-
- extern std::string mod_folder;
-
namespace environment
{
launcher::mode get_mode();
@@ -35,7 +30,7 @@ namespace game
T* get() const
{
- return reinterpret_cast((uint64_t)address_ + base_address);
+ return reinterpret_cast(address_);
}
operator T* () const
@@ -53,6 +48,4 @@ namespace game
};
}
-uintptr_t operator"" _b(const uintptr_t ptr);
-
-#include "symbols.hpp"
\ No newline at end of file
+#include "symbols.hpp"
diff --git a/src/client/game/scripting/functions.cpp b/src/client/game/scripting/functions.cpp
index c6977ad1..0a3da8b3 100644
--- a/src/client/game/scripting/functions.cpp
+++ b/src/client/game/scripting/functions.cpp
@@ -59,15 +59,15 @@ namespace scripting
script_function get_function_by_index(const unsigned index)
{
- static const auto function_table = 0xB153F90;
- static const auto method_table = 0xB155890;
+ static const auto function_table = 0x14B153F90;
+ static const auto method_table = 0x14B155890;
if (index < 0x320)
{
- return reinterpret_cast(game::base_address + function_table)[index - 1];
+ return reinterpret_cast(function_table)[index - 1];
}
- return reinterpret_cast(game::base_address + method_table)[index - 0x8000];
+ return reinterpret_cast(method_table)[index - 0x8000];
}
}
diff --git a/src/client/game/scripting/lua/context.cpp b/src/client/game/scripting/lua/context.cpp
index 0bee5fba..f35bf522 100644
--- a/src/client/game/scripting/lua/context.cpp
+++ b/src/client/game/scripting/lua/context.cpp
@@ -9,7 +9,6 @@
#include "../../../component/notifies.hpp"
#include "../../../component/scripting.hpp"
#include "../../../component/command.hpp"
-#include "../../../component/chat.hpp"
#include "../../../component/fastfiles.hpp"
#include
@@ -48,16 +47,6 @@ namespace scripting::lua
state["level"] = entity{*::game::levelEntityId};
state["player"] = call("getentbynum", {0}).as();
- state["io"]["fileexists"] = utils::io::file_exists;
- state["io"]["writefile"] = utils::io::write_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"]["readfile"] = static_cast(utils::io::read_file);
-
auto vector_type = state.new_usertype("vector", sol::constructors());
vector_type["x"] = sol::property(&vector::get_x, &vector::set_x);
vector_type["y"] = sol::property(&vector::get_y, &vector::set_y);
@@ -198,8 +187,8 @@ namespace scripting::lua
void setup_entity_type(sol::state& state, event_handler& handler, scheduler& scheduler)
{
- state["level"] = entity{ *::game::levelEntityId };
- state["player"] = call("getentbynum", { 0 }).as();
+ state["level"] = entity{*::game::levelEntityId};
+ state["player"] = call("getentbynum", {0}).as();
auto entity_type = state.new_usertype("entity");
@@ -387,9 +376,8 @@ namespace scripting::lua
command::execute(utils::string::va("setdiscordstate %s", state.data()), false);
};
- game_type["say"] = [](const game&, const std::string& msg)
+ game_type["say"] = [](const game&)
{
- chat::print(msg);
};
game_type["detour"] = [](const game&, const sol::this_state s, const std::string& filename,
diff --git a/src/client/game/scripting/lua/engine.cpp b/src/client/game/scripting/lua/engine.cpp
index 58008313..d9f0ae0e 100644
--- a/src/client/game/scripting/lua/engine.cpp
+++ b/src/client/game/scripting/lua/engine.cpp
@@ -3,6 +3,7 @@
#include "context.hpp"
#include "../../../component/notifies.hpp"
+#include "../../../component/filesystem.hpp"
#include "../execution.hpp"
#include
@@ -49,13 +50,9 @@ namespace scripting::lua::engine
load_generic_script();
- load_scripts("scripts/");
- load_scripts("h2-mod/scripts/");
- load_scripts("data/scripts/");
-
- if (!game::mod_folder.empty())
+ for (const auto& path : filesystem::get_search_paths())
{
- load_scripts(utils::string::va("%s/scripts/", game::mod_folder.data()));
+ load_scripts(path + "/scripts/");
}
}
diff --git a/src/client/game/scripting/lua/event_handler.cpp b/src/client/game/scripting/lua/event_handler.cpp
index 6c9286bb..d433fd18 100644
--- a/src/client/game/scripting/lua/event_handler.cpp
+++ b/src/client/game/scripting/lua/event_handler.cpp
@@ -35,23 +35,26 @@ namespace scripting::lua
for (auto i = tasks.begin(); i != tasks.end();)
{
+ if (i->is_deleted)
+ {
+ i = tasks.erase(i);
+ continue;
+ }
+
if (i->event != event.name || i->entity != event.entity)
{
++i;
continue;
}
- if (!i->is_deleted)
+ if (!has_built_arguments)
{
- if (!has_built_arguments)
- {
- has_built_arguments = true;
- arguments = this->build_arguments(event);
- }
-
- handle_error(i->callback(sol::as_args(arguments)));
+ has_built_arguments = true;
+ arguments = this->build_arguments(event);
}
+ handle_error(i->callback(sol::as_args(arguments)));
+
if (i->is_volatile || i->is_deleted)
{
i = tasks.erase(i);
@@ -94,8 +97,8 @@ namespace scripting::lua
callbacks_.access([&](task_list& tasks)
{
- merger(tasks);
- new_callbacks_.access(merger);
+ merger(tasks);
+ new_callbacks_.access(merger);
});
}
diff --git a/src/client/game/scripting/lua/event_handler.hpp b/src/client/game/scripting/lua/event_handler.hpp
index 981392e7..99009017 100644
--- a/src/client/game/scripting/lua/event_handler.hpp
+++ b/src/client/game/scripting/lua/event_handler.hpp
@@ -44,7 +44,7 @@ namespace scripting::lua
sol::state& state_;
std::atomic_int64_t current_listener_id_ = 0;
- using task_list = std::vector;
+ using task_list = std::list;
utils::concurrency::container new_callbacks_;
utils::concurrency::container callbacks_;
diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp
index 291203a8..8c3017c2 100644
--- a/src/client/game/structs.hpp
+++ b/src/client/game/structs.hpp
@@ -126,11 +126,90 @@ namespace game
// ...
};
+ struct GfxImage;
+
+ union MaterialTextureDefInfo
+ {
+ GfxImage* image;
+ void* water;
+ };
+
+ struct MaterialTextureDef
+ {
+ unsigned int nameHash;
+ char nameStart;
+ char nameEnd;
+ char samplerState;
+ char semantic;
+ MaterialTextureDefInfo u;
+ };
+
+ struct MaterialPass
+ {
+ void* vertexShader;
+ void* vertexDecl;
+ void* hullShader;
+ void* domainShader;
+ void* pixelShader;
+ char pixelOutputMask;
+ char perPrimArgCount;
+ char perObjArgCount;
+ char stableArgCount;
+ unsigned __int16 perPrimArgSize;
+ unsigned __int16 perObjArgSize;
+ unsigned __int16 stableArgSize;
+ char zone;
+ char perPrimConstantBuffer;
+ char perObjConstantBuffer;
+ char stableConstantBuffer;
+ unsigned int customBufferFlags;
+ char customSamplerFlags;
+ char precompiledIndex;
+ char stageConfig;
+ void* args;
+ };
+
+ struct MaterialTechnique
+ {
+ const char* name;
+ unsigned __int16 flags;
+ unsigned __int16 passCount;
+ MaterialPass passArray[1];
+ };
+
+ struct MaterialTechniqueSet
+ {
+ const char* name;
+ unsigned __int16 flags;
+ char worldVertFormat;
+ char preDisplacementOnlyCount;
+ MaterialTechnique* techniques[309];
+ };
+
+ struct GfxStateBits
+ {
+ unsigned int loadBits[3];
+ char zone;
+ char depthStencilState[11];
+ char blendState;
+ char rasterizerState;
+ };
+
struct Material
{
const char* name;
+ char __pad0[0x124];
+ char textureCount;
+ char __pad1[0xB];
+ MaterialTechniqueSet* techniqueSet;
+ MaterialTextureDef* textureTable;
+ void* constantTable;
+ GfxStateBits* stateBitsTable;
+ char __pad2[0x118];
};
+ static_assert(sizeof(Material) == 0x270);
+
struct point
{
float x;
@@ -678,6 +757,14 @@ namespace game
const char* buffer;
};
+ struct TTF
+ {
+ const char* name;
+ int len;
+ const char* buffer;
+ int fontFace;
+ };
+
union XAssetHeader
{
void* data;
@@ -687,6 +774,7 @@ namespace game
ScriptFile* scriptfile;
StringTable* stringTable;
LuaFile* luaFile;
+ TTF* ttf;
};
struct XAsset
diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp
index f9108c0a..6a885aa0 100644
--- a/src/client/game/symbols.hpp
+++ b/src/client/game/symbols.hpp
@@ -6,198 +6,210 @@ namespace game
{
// Functions
- WEAK symbol AddRefToValue{0x5C0EB0};
- WEAK symbol AddRefToObject{0x5C0EA0};
- WEAK symbol AllocThread{0x5C1200};
- WEAK symbol AllocVariable{0x5C1260};
- WEAK symbol RemoveRefToValue{0x5C29B0};
- WEAK symbol RemoveRefToObject{0x5C28A0};
+ WEAK symbol AddRefToValue{0x1405C0EB0};
+ WEAK symbol AddRefToObject{0x1405C0EA0};
+ WEAK symbol AllocThread{0x1405C1200};
+ WEAK symbol AllocVariable{0x1405C1260};
+ WEAK symbol RemoveRefToValue{0x1405C29B0};
+ WEAK symbol RemoveRefToObject{0x1405C28A0};
WEAK symbol BG_GetWeaponNameComplete{0x6A0800};
+ char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x1406A0800};
- WEAK symbol Cbuf_AddText{0x59A050};
+ WEAK symbol Cbuf_AddText{0x14059A050};
- WEAK symbol CG_GameMessage{0x37F450};
- WEAK symbol CG_GameMessageBold{0x37F1B0};
+ WEAK symbol CG_GameMessage{0x14037F450};
+ WEAK symbol CG_GameMessageBold{0x14037F1B0};
WEAK symbol CG_GetWeaponDisplayName{0x3B9210};
+ bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x1403B9210};
- WEAK symbol Cmd_AddCommandInternal{0x59A5F0};
- WEAK symbol Cmd_ExecuteSingleCommand{0x59ABA0};
+ WEAK symbol Cmd_AddCommandInternal{0x14059A5F0};
+ WEAK symbol Cmd_ExecuteSingleCommand{0x14059ABA0};
- WEAK symbol Com_Error{0x5A2D80};
- WEAK symbol Com_Quit_f{0x5A50D0};
- WEAK symbol Quit{0x5A52A0};
+ WEAK symbol Com_Error{0x1405A2D80};
+ WEAK symbol Com_Shutdown{0x1405A62C0};
+ WEAK symbol Com_Quit_f{0x1405A50D0};
+ WEAK symbol Com_InFrontend{0x140328BD0};
+ WEAK symbol Quit{0x1405A52A0};
WEAK symbol
- DB_EnumXAssets_Internal{0x4129F0};
- WEAK symbol DB_GetXAssetName{0x3E4090};
- WEAK symbol DB_LoadXAssets{0x414FF0};
- WEAK symbol DB_FindXAssetHeader{0x412F60};
- WEAK symbol DB_GetRawFileLen{0x413D80};
- WEAK symbol DB_GetRawBuffer{0x413C40};
+ DB_EnumXAssets_Internal{0x1404129F0};
+ WEAK symbol DB_GetXAssetName{0x1403E4090};
+ WEAK symbol DB_LoadXAssets{0x140414FF0};
+ WEAK symbol DB_FindXAssetHeader{0x140412F60};
+ WEAK symbol DB_GetRawFileLen{0x140413D80};
+ WEAK symbol DB_GetRawBuffer{0x140413C40};
- WEAK symbol Dvar_FindVar{0x618F90};
- WEAK symbol Dvar_GetCombinedString{0x5A75D0};
- WEAK symbol Dvar_RegisterBool{0x617BB0};
- WEAK symbol Dvar_RegisterInt{0x618090};
+ WEAK symbol Dvar_FindVar{0x140618F90};
+ WEAK symbol Dvar_GetCombinedString{0x1405A75D0};
+ WEAK symbol Dvar_RegisterBool{0x140617BB0};
+ WEAK symbol Dvar_RegisterInt{0x140618090};
WEAK symbol
- Dvar_RegisterFloat{0x617F80};
+ Dvar_RegisterFloat{0x140617F80};
WEAK symbol
- Dvar_RegisterString{0x618170};
+ Dvar_RegisterString{0x140618170};
WEAK symbol Dvar_RegisterVec4{0x6185F0};
- WEAK symbol Dvar_DisplayableValue{0x618EA0};
- WEAK symbol Dvar_ValueToString{0x61B8F0};
- WEAK symbol Dvar_SetCommand{0x61A5C0};
- WEAK symbol Dvar_SetFromStringFromSource{0x61A910};
+ unsigned int flags)> Dvar_RegisterVec4{0x1406185F0};
+ WEAK symbol Dvar_DisplayableValue{0x140618EA0};
+ WEAK symbol Dvar_ValueToString{0x14061B8F0};
+ WEAK symbol Dvar_SetCommand{0x14061A5C0};
+ WEAK symbol Dvar_SetFromStringFromSource{0x14061A910};
- WEAK symbol generateHashValue{0x343D20};
+ WEAK symbol generateHashValue{0x140343D20};
- WEAK symbol CL_IsCgameInitialized{0x3CA0C0};
+ WEAK symbol CL_IsCgameInitialized{0x1403CA0C0};
WEAK symbol CL_DrawTextPhysicalWithEffects{0x3D4990};
+ int fxBirthTime, int fxLetterTime, int fxDecayStartTime, int fxDecayDuration, int a17)> CL_DrawTextPhysicalWithEffects{0x1403D4990};
- WEAK symbol FindVariable{0x5C1D50};
- WEAK symbol FindEntityId{0x5C1C50};
- WEAK symbol GetEntityFieldValue{0x5C6100};
- WEAK symbol GetVariable{0x5C2690};
- WEAK symbol GetNewVariable{0x5C22B0};
- WEAK symbol GetNewArrayVariable{0x5C2130};
- WEAK symbol SetNewVariableValue{0x5C5EA0};
- WEAK symbol RemoveVariableValue{0x5C2A50};
+ WEAK symbol FindVariable{0x1405C1D50};
+ WEAK symbol FindEntityId{0x1405C1C50};
+ WEAK symbol GetEntityFieldValue{0x1405C6100};
+ WEAK symbol GetVariable{0x1405C2690};
+ WEAK symbol GetNewVariable{0x1405C22B0};
+ WEAK symbol GetNewArrayVariable{0x1405C2130};
+ WEAK symbol SetNewVariableValue{0x1405C5EA0};
+ WEAK symbol RemoveVariableValue{0x1405C2A50};
- WEAK symbol G_GetWeaponForName{0x51B260};
+ WEAK symbol G_GetWeaponForName{0x14051B260};
WEAK symbol
- G_GivePlayerWeapon{0x51B660};
- WEAK symbol G_InitializeAmmo{0x4C4110};
- WEAK symbol G_SelectWeapon{0x51C0D0};
- WEAK symbol WorldPosToScreenPos{0x36F310};
+ G_GivePlayerWeapon{0x14051B660};
+ WEAK symbol G_InitializeAmmo{0x1404C4110};
+ WEAK symbol G_SelectWeapon{0x14051C0D0};
+ WEAK symbol WorldPosToScreenPos{0x14036F310};
- WEAK symbol I_CleanStr{0x620660};
+ WEAK symbol I_CleanStr{0x140620660};
WEAK symbol Image_Setup{0x74B2A0};
+ uint32_t imageFlags, DXGI_FORMAT imageFormat, int a8, const char* name, const void* initData)> Image_Setup{0x14074B2A0};
- WEAK symbol Key_KeynumToString{0x3D32D0};
+ WEAK symbol Key_KeynumToString{0x1403D32D0};
- WEAK symbol LUI_OpenMenu{0x5F0EE0};
- WEAK symbol Menu_IsMenuOpenAndVisible{0x5EE1A0};
+ WEAK symbol LUI_OpenMenu{0x1405F0EE0};
+ WEAK symbol LUI_BeginEvent{0x1403155E0};
+ WEAK symbol LUI_EndEvent{0x140316890};
+ WEAK symbol LUI_EnterCriticalSection{0x140316980};
+ WEAK symbol LUI_LeaveCriticalSection{0x14031BC20};
+ WEAK symbol Menu_IsMenuOpenAndVisible{0x1405EE1A0};
- WEAK symbol Material_RegisterHandle{0x759BA0};
+ WEAK symbol Material_RegisterHandle{0x140759BA0};
- WEAK symbol PathNode_WorldifyPosFromParent{0x525830};
+ WEAK symbol