Merge branch 'develop' into scripting
This commit is contained in:
commit
7ba8b4d596
@ -12,7 +12,7 @@ Proof of concept for an Modern Warfare Remastered client. <br>
|
||||
## Compile from source
|
||||
|
||||
- Clone the Git repo. Do NOT download it as ZIP, that won't work.
|
||||
- Update the submodules and run `premake5 vs2019` or simply use the delivered `generate.bat`.
|
||||
- Update the submodules and run `premake5 vs2022` or simply use the delivered `generate.bat`.
|
||||
- Build via solution file in `build\h1-mod.sln`.
|
||||
|
||||
### Premake arguments
|
||||
|
0
data/.gitkeep
Normal file
0
data/.gitkeep
Normal file
143
data/ui_scripts/server_list/__init__.lua
Normal file
143
data/ui_scripts/server_list/__init__.lua
Normal file
@ -0,0 +1,143 @@
|
||||
local Lobby = luiglobals.Lobby
|
||||
local SystemLinkJoinMenu = LUI.mp_menus.SystemLinkJoinMenu
|
||||
|
||||
if (not SystemLinkJoinMenu) then
|
||||
return
|
||||
end
|
||||
|
||||
local offsets = {
|
||||
10,
|
||||
500,
|
||||
950,
|
||||
700,
|
||||
1100,
|
||||
}
|
||||
|
||||
local columns = {
|
||||
"@MENU_HOST_NAME",
|
||||
"@MENU_MAP",
|
||||
"Players",
|
||||
"@MENU_TYPE1",
|
||||
"Ping",
|
||||
}
|
||||
|
||||
SystemLinkJoinMenu.AddHeaderButton = function(menu, f12_arg1, width)
|
||||
local state = CoD.CreateState(0, f12_arg1, nil, nil, CoD.AnchorTypes.TopLeft)
|
||||
state.width = width
|
||||
local element = LUI.UIElement.new(state)
|
||||
local button = SystemLinkJoinMenu.CreateButton("header", 24)
|
||||
|
||||
button:addElement(LUI.Divider.new(CoD.CreateState(nil, 0, nil, nil, CoD.AnchorTypes.TopLeftRight), 40, LUI.Divider.Grey))
|
||||
button:makeNotFocusable()
|
||||
button:addElement(LUI.Divider.new(CoD.CreateState(nil, 0, nil, nil, CoD.AnchorTypes.BottomLeftRight), 40, LUI.Divider.Grey))
|
||||
|
||||
local gettext = function(i)
|
||||
return Engine.Localize(columns[i])
|
||||
end
|
||||
|
||||
for i = 1, #offsets do
|
||||
SystemLinkJoinMenu.MakeText(button.textHolder, offsets[i], gettext(i), nil)
|
||||
end
|
||||
|
||||
element:addElement(button)
|
||||
menu:addElement(element)
|
||||
end
|
||||
|
||||
SystemLinkJoinMenu.AddServerButton = function(menu, controller, index)
|
||||
local button = SystemLinkJoinMenu.CreateButton(index or "header", 24)
|
||||
button:makeFocusable()
|
||||
button.index = index
|
||||
button:addEventHandler("button_action", SystemLinkJoinMenu.OnJoinGame)
|
||||
|
||||
local gettext = function(i)
|
||||
return Lobby.GetServerData(controller, index, i - 1)
|
||||
end
|
||||
|
||||
for i = 1, #offsets do
|
||||
SystemLinkJoinMenu.MakeText(button.textHolder, offsets[i], gettext(i), luiglobals.Colors.h1.medium_grey)
|
||||
end
|
||||
|
||||
menu.list:addElement(button)
|
||||
return button
|
||||
end
|
||||
|
||||
SystemLinkJoinMenu.MakeText = function(menu, f5_arg1, text, color)
|
||||
local state = CoD.CreateState(f5_arg1, nil, f5_arg1 + 200, nil, CoD.AnchorTypes.Left)
|
||||
state.font = CoD.TextSettings.TitleFontSmall.Font
|
||||
state.top = -6
|
||||
state.height = 14
|
||||
state.alignment = LUI.Alignment.Left
|
||||
state.glow = LUI.GlowState.None
|
||||
state.color = color
|
||||
|
||||
local el = LUI.UIText.new(state)
|
||||
el:registerAnimationState("focused", {
|
||||
color = luiglobals.Colors.white
|
||||
})
|
||||
|
||||
el:registerEventHandler("focused", function(element, event)
|
||||
element:animateToState("focused", 0)
|
||||
end)
|
||||
|
||||
el:registerEventHandler("unfocused", function(element, event)
|
||||
element:animateToState("default", 0)
|
||||
end)
|
||||
|
||||
el:setText(text)
|
||||
menu:addElement(el)
|
||||
end
|
||||
|
||||
function menu_systemlink_join(f19_arg0, f19_arg1)
|
||||
local width = 1145
|
||||
|
||||
local menu = LUI.MenuTemplate.new(f19_arg0, {
|
||||
menu_title = "@PLATFORM_SYSTEM_LINK_TITLE",
|
||||
menu_width = width,
|
||||
menu_top_indent = 20,
|
||||
disableDeco = true,
|
||||
spacing = 1
|
||||
})
|
||||
|
||||
SystemLinkJoinMenu.AddHeaderButton(menu, 80, width)
|
||||
SystemLinkJoinMenu.AddLowerCounter(menu, width)
|
||||
SystemLinkJoinMenu.UpdateCounterText(menu, nil)
|
||||
Lobby.BuildServerList(Engine.GetFirstActiveController())
|
||||
|
||||
menu.list:registerEventHandler(LUI.UIScrollIndicator.UpdateEvent, function(element, event)
|
||||
SystemLinkJoinMenu.UpdateCounterText(menu, event)
|
||||
end)
|
||||
|
||||
SystemLinkJoinMenu.UpdateGameList(menu)
|
||||
menu:registerEventHandler("updateGameList", SystemLinkJoinMenu.UpdateGameList)
|
||||
menu:addElement(LUI.UITimer.new(250, "updateGameList"))
|
||||
|
||||
LUI.ButtonHelperText.ClearHelperTextObjects(menu.help, {
|
||||
side = "all"
|
||||
})
|
||||
|
||||
menu:AddHelp({
|
||||
name = "add_button_helper_text",
|
||||
button_ref = "button_alt1",
|
||||
helper_text = Engine.Localize("@MENU_SB_TOOLTIP_BTN_REFRESH"),
|
||||
side = "right",
|
||||
clickable = true,
|
||||
priority = -1000
|
||||
}, function(f21_arg0, f21_arg1)
|
||||
SystemLinkJoinMenu.RefreshServers(f21_arg0, f21_arg1, menu)
|
||||
end)
|
||||
|
||||
menu:AddHelp({
|
||||
name = "add_button_helper_text",
|
||||
button_ref = "button_action",
|
||||
helper_text = Engine.Localize("@MENU_JOIN_GAME1"),
|
||||
side = "left",
|
||||
clickable = false,
|
||||
priority = -1000
|
||||
}, nil, nil, true)
|
||||
|
||||
menu:AddBackButton()
|
||||
|
||||
return menu
|
||||
end
|
||||
|
||||
LUI.MenuBuilder.m_types_build["menu_systemlink_join"] = menu_systemlink_join
|
@ -1,3 +1,3 @@
|
||||
@echo off
|
||||
git submodule update --init --recursive
|
||||
tools\premake5 %* vs2019
|
||||
tools\premake5 %* vs2022
|
37
premake5.lua
37
premake5.lua
@ -227,10 +227,12 @@ targetdir "%{wks.location}/bin/%{cfg.platform}/%{cfg.buildcfg}"
|
||||
|
||||
configurations {"Debug", "Release"}
|
||||
|
||||
architecture "x64"
|
||||
language "C++"
|
||||
cppdialect "C++20"
|
||||
|
||||
architecture "x86_64"
|
||||
platforms "x64"
|
||||
|
||||
buildoptions "/std:c++latest"
|
||||
systemversion "latest"
|
||||
symbols "On"
|
||||
staticruntime "On"
|
||||
@ -248,25 +250,22 @@ end
|
||||
|
||||
flags {"NoIncrementalLink", "NoMinimalRebuild", "MultiProcessorCompile", "No64BitChecks"}
|
||||
|
||||
filter "platforms:x64"
|
||||
defines {"_WINDOWS", "WIN32"}
|
||||
filter {}
|
||||
|
||||
configuration "windows"
|
||||
defines {"_WINDOWS", "WIN32"}
|
||||
filter "configurations:Release"
|
||||
optimize "Size"
|
||||
buildoptions {"/GL"}
|
||||
linkoptions { "/IGNORE:4702", "/LTCG" }
|
||||
defines {"NDEBUG"}
|
||||
flags {"FatalCompileWarnings"}
|
||||
filter {}
|
||||
|
||||
configuration "Release"
|
||||
optimize "Size"
|
||||
buildoptions {"/GL"}
|
||||
linkoptions { "/IGNORE:4702", "/LTCG" }
|
||||
|
||||
defines {"NDEBUG"}
|
||||
|
||||
flags {"FatalCompileWarnings"}
|
||||
|
||||
configuration "Debug"
|
||||
optimize "Debug"
|
||||
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
|
||||
configuration {}
|
||||
filter "configurations:Debug"
|
||||
optimize "Debug"
|
||||
defines {"DEBUG", "_DEBUG"}
|
||||
filter {}
|
||||
|
||||
project "common"
|
||||
kind "StaticLib"
|
||||
|
@ -254,7 +254,7 @@ namespace console
|
||||
{
|
||||
std::string cmd;
|
||||
|
||||
while (true)
|
||||
while (!this->terminate_runner_)
|
||||
{
|
||||
std::getline(std::cin, cmd);
|
||||
command::execute(cmd);
|
||||
|
@ -196,9 +196,6 @@ namespace dedicated
|
||||
// Disable r_preloadShaders
|
||||
dvars::override::register_bool("r_preloadShaders", false, game::DVAR_FLAG_READ);
|
||||
|
||||
// Don't allow sv_hostname to be changed by the game
|
||||
dvars::disable::set_string("sv_hostname");
|
||||
|
||||
// Stop crashing from sys_errors
|
||||
utils::hook::jump(0x140511520, sys_error_stub);
|
||||
|
||||
|
@ -49,17 +49,21 @@ namespace discord
|
||||
|
||||
discord_presence.details = utils::string::va("%s on %s", gametype, mapname);
|
||||
|
||||
auto host_name = game::Dvar_FindVar("sv_hostname")->current.string;
|
||||
auto max_clients = game::Dvar_FindVar("sv_maxclients")->current.integer;
|
||||
char clean_hostname[0x100] = {0};
|
||||
utils::string::strip(game::Dvar_FindVar("sv_hostname")->current.string,
|
||||
clean_hostname, sizeof(clean_hostname));
|
||||
auto max_clients = party::server_client_count();
|
||||
|
||||
// When true, we are in Private Match
|
||||
if (game::SV_Loaded())
|
||||
{
|
||||
max_clients = party::server_client_count();
|
||||
strcpy_s(clean_hostname, "Private Match");
|
||||
max_clients = game::Dvar_FindVar("sv_maxclients")->current.integer;
|
||||
}
|
||||
|
||||
auto clients = *(reinterpret_cast<int*>(0x14621BE00));
|
||||
discord_presence.partySize = clients;
|
||||
discord_presence.partySize = *reinterpret_cast<int*>(0x1429864C4);
|
||||
discord_presence.partyMax = max_clients;
|
||||
discord_presence.state = host_name;
|
||||
discord_presence.state = clean_hostname;
|
||||
discord_presence.largeImageKey = map;
|
||||
}
|
||||
else if (game::environment::is_sp())
|
||||
|
@ -435,7 +435,7 @@ namespace dvars
|
||||
dvar_set_float_hook.create(SELECT_VALUE(0x1403C7420, 0x1404FD360), &dvar_set_float);
|
||||
dvar_set_int_hook.create(SELECT_VALUE(0x1403C76C0, 0x1404FD5E0), &dvar_set_int);
|
||||
dvar_set_string_hook.create(SELECT_VALUE(0x1403C7900, 0x1404FD8D0), &dvar_set_string);
|
||||
dvar_set_from_string_hook.create(SELECT_VALUE(0, 0x1404FD520), &dvar_set_from_string);
|
||||
dvar_set_from_string_hook.create(SELECT_VALUE(0x1403C7620, 0x1404FD520), &dvar_set_from_string);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
#include <version.hpp>
|
||||
|
||||
#include "game/dvars.hpp"
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
namespace exception
|
||||
{
|
||||
@ -255,17 +254,6 @@ namespace exception
|
||||
{
|
||||
dvars::cg_legacyCrashHandling = dvars::register_bool("cg_legacyCrashHandling",
|
||||
false, game::DVAR_FLAG_SAVED, true);
|
||||
|
||||
// Could cause memory leaks but fixes possible out of memory (12) errors
|
||||
const auto has_flag = utils::flags::has_flag("memoryfix");
|
||||
if (has_flag)
|
||||
{
|
||||
utils::hook::jump(0x140578BE0, malloc);
|
||||
utils::hook::jump(0x140578B00, _aligned_malloc);
|
||||
utils::hook::jump(0x140578C40, free);
|
||||
utils::hook::jump(0x140578D30, realloc);
|
||||
utils::hook::jump(0x140578B60, _aligned_realloc);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
49
src/client/component/fastfiles.cpp
Normal file
49
src/client/component/fastfiles.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "fastfiles.hpp"
|
||||
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
namespace fastfiles
|
||||
{
|
||||
static utils::concurrency::container<std::string> current_fastfile;
|
||||
|
||||
namespace
|
||||
{
|
||||
utils::hook::detour db_try_load_x_file_internal_hook;
|
||||
|
||||
void db_try_load_x_file_internal(const char* zone_name, const int flags)
|
||||
{
|
||||
printf("Loading fastfile %s\n", zone_name);
|
||||
current_fastfile.access([&](std::string& fastfile)
|
||||
{
|
||||
fastfile = zone_name;
|
||||
});
|
||||
db_try_load_x_file_internal_hook.invoke<void>(zone_name, flags);
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_current_fastfile()
|
||||
{
|
||||
return current_fastfile.access<std::string>([&](std::string& fastfile)
|
||||
{
|
||||
return fastfile;
|
||||
});
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
db_try_load_x_file_internal_hook.create(
|
||||
SELECT_VALUE(0x1401CDDD0, 0x1402BFFE0), &db_try_load_x_file_internal);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(fastfiles::component)
|
8
src/client/component/fastfiles.hpp
Normal file
8
src/client/component/fastfiles.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace fastfiles
|
||||
{
|
||||
std::string get_current_fastfile();
|
||||
}
|
@ -555,7 +555,7 @@ namespace game_console
|
||||
{
|
||||
if (key == game::keyNum_t::K_F10)
|
||||
{
|
||||
if (game::mp::svs_clients[local_client_num].header.state >= 1)
|
||||
if (!game::Com_InFrontEnd())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -15,11 +15,6 @@ namespace lui
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
if (!game::environment::is_mp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't show create cod account popup
|
||||
//utils::hook::set<uint32_t>(0x14017C957, 0); // H1(1.4)
|
||||
|
||||
|
@ -27,12 +27,19 @@ namespace network
|
||||
const auto cmd_string = utils::string::to_lower(command);
|
||||
auto& callbacks = get_callbacks();
|
||||
const auto handler = callbacks.find(cmd_string);
|
||||
|
||||
if (handler == callbacks.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto offset = cmd_string.size() + 5;
|
||||
|
||||
if (message->cursize <= offset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string_view data(message->data + offset, message->cursize - offset);
|
||||
|
||||
handler->second(*address, data);
|
||||
|
@ -2,18 +2,18 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "dvars.hpp"
|
||||
#include "version.h"
|
||||
#include "command.hpp"
|
||||
#include "console.hpp"
|
||||
#include "network.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
#include <component/scheduler.hpp>
|
||||
|
||||
#include "version.h"
|
||||
#include <component/command.hpp>
|
||||
#include <component/console.hpp>
|
||||
#include <component/network.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
|
||||
namespace patches
|
||||
{
|
||||
@ -258,6 +258,19 @@ namespace patches
|
||||
|
||||
// Prevent clients from sending invalid reliableAcknowledge
|
||||
// utils::hook::call(0x1404899C6, sv_execute_client_message_stub); // H1(1.4)
|
||||
|
||||
// "fix" for rare 'Out of memory error' error
|
||||
if (utils::flags::has_flag("memoryfix"))
|
||||
{
|
||||
utils::hook::jump(0x140578BE0, malloc);
|
||||
utils::hook::jump(0x140578B00, _aligned_malloc);
|
||||
utils::hook::jump(0x140578C40, free);
|
||||
utils::hook::jump(0x140578D30, realloc);
|
||||
utils::hook::jump(0x140578B60, _aligned_realloc);
|
||||
}
|
||||
|
||||
// Change default hostname and make it replicated
|
||||
dvars::override::register_string("sv_hostname", "^2H1-Mod^7 Default Server", game::DVAR_FLAG_REPLICATED);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -151,6 +151,11 @@ namespace server_list
|
||||
return servers[i].game_type.empty() ? "" : utils::string::va("%s", servers[i].game_type.data());
|
||||
}
|
||||
|
||||
if (column == 4)
|
||||
{
|
||||
return servers[i].game_type.empty() ? "" : utils::string::va("%i", servers[i].ping);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
|
180
src/client/component/ui_scripting.cpp
Normal file
180
src/client/component/ui_scripting.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
#include "ui_scripting.hpp"
|
||||
|
||||
#include "game/ui_scripting/lua/engine.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
#include "game/ui_scripting/lua/error.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
namespace
|
||||
{
|
||||
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions;
|
||||
|
||||
utils::hook::detour hksi_lual_error_hook;
|
||||
utils::hook::detour hksi_lual_error_hook2;
|
||||
utils::hook::detour hks_start_hook;
|
||||
utils::hook::detour hks_shutdown_hook;
|
||||
utils::hook::detour hks_allocator_hook;
|
||||
utils::hook::detour hks_frame_hook;
|
||||
|
||||
bool error_hook_enabled = false;
|
||||
|
||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
||||
{
|
||||
char va_buffer[2048] = {0};
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
vsprintf_s(va_buffer, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
const auto formatted = std::string(va_buffer);
|
||||
|
||||
if (!error_hook_enabled)
|
||||
{
|
||||
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(formatted);
|
||||
}
|
||||
}
|
||||
|
||||
void* hks_start_stub(char a1)
|
||||
{
|
||||
const auto _1 = gsl::finally([]()
|
||||
{
|
||||
ui_scripting::lua::engine::start();
|
||||
});
|
||||
|
||||
return hks_start_hook.invoke<void*>(a1);
|
||||
}
|
||||
|
||||
void hks_shutdown_stub()
|
||||
{
|
||||
ui_scripting::lua::engine::stop();
|
||||
hks_shutdown_hook.invoke<void*>();
|
||||
}
|
||||
|
||||
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
|
||||
{
|
||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory);
|
||||
if (converted_functions.find(closure) != converted_functions.end())
|
||||
{
|
||||
converted_functions.erase(closure);
|
||||
}
|
||||
|
||||
return hks_allocator_hook.invoke<void*>(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)
|
||||
{
|
||||
const auto value = state->m_apistack.base[-1];
|
||||
if (value.t != game::hks::TCFUNCTION)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(value.v.cClosure);
|
||||
if (converted_functions.find(closure) == converted_functions.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto function = converted_functions[closure];
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
const auto arguments = get_return_values(count);
|
||||
const auto s = function.lua_state();
|
||||
|
||||
std::vector<sol::lua_value> converted_args;
|
||||
|
||||
for (const auto& argument : arguments)
|
||||
{
|
||||
converted_args.push_back(lua::convert(s, argument));
|
||||
}
|
||||
|
||||
const auto results = function(sol::as_args(converted_args));
|
||||
lua::handle_error(results);
|
||||
|
||||
for (const auto& result : results)
|
||||
{
|
||||
push_value(lua::convert({s, result}));
|
||||
}
|
||||
|
||||
return results.return_count();
|
||||
}
|
||||
|
||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function)
|
||||
{
|
||||
converted_functions[closure] = function;
|
||||
}
|
||||
|
||||
void clear_converted_functions()
|
||||
{
|
||||
converted_functions.clear();
|
||||
}
|
||||
|
||||
void enable_error_hook()
|
||||
{
|
||||
error_hook_enabled = true;
|
||||
}
|
||||
|
||||
void disable_error_hook()
|
||||
{
|
||||
error_hook_enabled = false;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
if (game::environment::is_dedi())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
hks_start_hook.create(SELECT_VALUE(0x1400E4B40, 0x140176A40), hks_start_stub);
|
||||
hks_shutdown_hook.create(SELECT_VALUE(0x1400DD3D0, 0x14016CA80), hks_shutdown_stub);
|
||||
hksi_lual_error_hook.create(SELECT_VALUE(0x1400A5EA0, 0x14012F300), hksi_lual_error_stub);
|
||||
hks_allocator_hook.create(SELECT_VALUE(0x14009B570, 0x14012BAC0), hks_allocator_stub);
|
||||
hks_frame_hook.create(SELECT_VALUE(0x1400E37F0, 0x1401755B0), hks_frame_stub);
|
||||
|
||||
if (game::environment::is_mp())
|
||||
{
|
||||
hksi_lual_error_hook2.create(0x1401366B0, hksi_lual_error_stub);
|
||||
}
|
||||
|
||||
command::add("lui_restart", []()
|
||||
{
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400DD3D0, 0x14016CA80));
|
||||
utils::hook::invoke<void>(SELECT_VALUE(0x1400E6170, 0x1401780D0));
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(ui_scripting::component)
|
12
src/client/component/ui_scripting.hpp
Normal file
12
src/client/component/ui_scripting.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "game/ui_scripting/lua/value_conversion.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
int main_function_handler(game::hks::lua_State* state);
|
||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function);
|
||||
void clear_converted_functions();
|
||||
|
||||
void enable_error_hook();
|
||||
void disable_error_hook();
|
||||
}
|
@ -141,4 +141,33 @@ namespace demonware
|
||||
buffer->read_string(&this->timezone);
|
||||
}
|
||||
};
|
||||
|
||||
// made up name
|
||||
class bdFile final : public bdTaskResult
|
||||
{
|
||||
public:
|
||||
uint64_t owner_id;
|
||||
std::string platform;
|
||||
std::string filename;
|
||||
uint32_t unk;
|
||||
std::string data;
|
||||
|
||||
void serialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->write_uint64(this->owner_id);
|
||||
buffer->write_string(this->platform);
|
||||
buffer->write_string(this->filename);
|
||||
buffer->write_uint32(this->unk);
|
||||
buffer->write_blob(this->data);
|
||||
}
|
||||
|
||||
void deserialize(byte_buffer* buffer) override
|
||||
{
|
||||
buffer->read_uint64(&this->owner_id);
|
||||
buffer->read_string(&this->platform);
|
||||
buffer->read_string(&this->filename);
|
||||
buffer->read_uint32(&this->unk);
|
||||
buffer->read_blob(&this->data);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -66,17 +66,24 @@ namespace demonware
|
||||
|
||||
void bdStorage::list_publisher_files(service_server* server, byte_buffer* buffer)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
utils::io::write_file("demonware/bdStorage/list_publisher_files", buffer->get_buffer());
|
||||
#endif
|
||||
|
||||
uint32_t date;
|
||||
uint16_t num_results, offset;
|
||||
std::string filename, data;
|
||||
std::string unk, filename, data;
|
||||
|
||||
int out{};
|
||||
buffer->read(2, &out);
|
||||
buffer->read_string(&unk);
|
||||
buffer->read_uint32(&date);
|
||||
buffer->read_uint16(&num_results);
|
||||
buffer->read_uint16(&offset);
|
||||
buffer->read_string(&filename);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: list publisher files: %s\n", filename.data());
|
||||
#endif
|
||||
|
||||
auto reply = server->create_reply(this->task_id());
|
||||
|
||||
if (this->load_publisher_resource(filename, data))
|
||||
@ -99,9 +106,12 @@ namespace demonware
|
||||
|
||||
void bdStorage::get_publisher_file(service_server* server, byte_buffer* buffer)
|
||||
{
|
||||
std::string filename;
|
||||
int out{};
|
||||
buffer->read(2, &out);
|
||||
#ifdef DEBUG
|
||||
utils::io::write_file("demonware/bdStorage/get_publisher_file", buffer->get_buffer());
|
||||
#endif
|
||||
|
||||
std::string unk, filename;
|
||||
buffer->read_string(&unk);
|
||||
buffer->read_string(&filename);
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -133,15 +143,31 @@ namespace demonware
|
||||
|
||||
void bdStorage::set_user_file(service_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
bool priv;
|
||||
#ifdef DEBUG
|
||||
utils::io::write_file("demonware/bdStorage/set_user_file", buffer->get_buffer());
|
||||
#endif
|
||||
|
||||
uint64_t owner;
|
||||
std::string game, filename, data;
|
||||
uint32_t numfiles;
|
||||
std::string game, platform;
|
||||
|
||||
buffer->read_string(&game);
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_bool(&priv);
|
||||
buffer->read_blob(&data);
|
||||
buffer->read_uint64(&owner);
|
||||
buffer->read_string(&platform);
|
||||
buffer->read_uint32(&numfiles);
|
||||
|
||||
auto reply = server->create_reply(this->task_id());
|
||||
|
||||
for (uint32_t i = 0; i < numfiles; i++)
|
||||
{
|
||||
std::string filename, data;
|
||||
uint32_t unk;
|
||||
bool priv;
|
||||
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_blob(&data);
|
||||
buffer->read_uint32(&unk);
|
||||
buffer->read_bool(&priv);
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
utils::io::write_file(path, data);
|
||||
@ -153,35 +179,68 @@ namespace demonware
|
||||
info->create_time = uint32_t(time(nullptr));
|
||||
info->modified_time = info->create_time;
|
||||
info->file_size = uint32_t(data.size());
|
||||
info->owner_id = owner;
|
||||
info->owner_id = uint64_t(owner);
|
||||
info->priv = priv;
|
||||
|
||||
auto reply = server->create_reply(this->task_id());
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: set user file: %s\n", filename.data());
|
||||
#endif
|
||||
|
||||
reply->add(info);
|
||||
}
|
||||
|
||||
reply->send();
|
||||
}
|
||||
|
||||
void bdStorage::get_user_file(service_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
uint64_t owner{};
|
||||
std::string game, filename, platform, data;
|
||||
|
||||
int out{};
|
||||
buffer->read(2, &out);
|
||||
buffer->read_string(&game);
|
||||
buffer->read_string(&filename);
|
||||
buffer->read_uint64(&owner);
|
||||
buffer->read_string(&platform);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: user file: %s, %s, %s\n", game.data(), filename.data(), platform.data());
|
||||
utils::io::write_file("demonware/bdStorage/get_user_file", buffer->get_buffer());
|
||||
#endif
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
if (utils::io::read_file(path, &data))
|
||||
{
|
||||
uint32_t unk32_0;
|
||||
uint32_t numfiles, count = 0;
|
||||
uint64_t owner;
|
||||
std::string game, platform;
|
||||
|
||||
buffer->read_string(&game);
|
||||
buffer->read_uint32(&unk32_0);
|
||||
buffer->read_uint64(&owner);
|
||||
buffer->read_string(&platform);
|
||||
buffer->read_uint64(&owner);
|
||||
buffer->read_string(&platform);
|
||||
buffer->read_uint32(&numfiles);
|
||||
|
||||
auto reply = server->create_reply(this->task_id());
|
||||
reply->add(new bdFileData(data));
|
||||
|
||||
for (uint32_t i = 0; i < numfiles; i++)
|
||||
{
|
||||
std::string filename, data;
|
||||
buffer->read_string(&filename);
|
||||
|
||||
const auto path = get_user_file_path(filename);
|
||||
if (!utils::io::read_file(path, &data))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto response = new bdFile;
|
||||
response->owner_id = owner;
|
||||
response->unk = 0;
|
||||
response->platform = platform;
|
||||
response->filename = filename;
|
||||
response->data = data;
|
||||
|
||||
reply->add(response);
|
||||
++count;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("[DW]: [bdStorage]: get user file: %s, %s, %s\n", game.data(), filename.data(), platform.data());
|
||||
#endif
|
||||
}
|
||||
|
||||
if (count == numfiles)
|
||||
{
|
||||
reply->send();
|
||||
}
|
||||
else
|
||||
@ -192,6 +251,10 @@ namespace demonware
|
||||
|
||||
void bdStorage::unk12(service_server* server, byte_buffer* buffer) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
utils::io::write_file("demonware/bdStorage/unk12", buffer->get_buffer());
|
||||
#endif
|
||||
|
||||
// TODO:
|
||||
auto reply = server->create_reply(this->task_id());
|
||||
reply->send();
|
||||
|
@ -1397,7 +1397,7 @@ namespace game
|
||||
LiveClientDropType liveDropRequest; //269572
|
||||
char __pad4[24];
|
||||
TestClientType testClient; // 269600
|
||||
char __pad5[391700];
|
||||
char __pad5[610012];
|
||||
}; // size = 661304
|
||||
}
|
||||
|
||||
|
@ -29,8 +29,9 @@ namespace game
|
||||
|
||||
WEAK symbol<void(unsigned int weapon, bool isAlternate, char* output, unsigned int maxStringLen)> BG_GetWeaponNameComplete{0x0, 0x140165580}; // H1MP
|
||||
|
||||
WEAK symbol<void()> Com_Frame_Try_Block_Function{0, 0x1400D8310};
|
||||
WEAK symbol<void()> Com_Frame_Try_Block_Function{0x1401CE8D0, 0x1400D8310};
|
||||
WEAK symbol<CodPlayMode()> Com_GetCurrentCoDPlayMode{0, 0x1405039A0};
|
||||
WEAK symbol<bool()> Com_InFrontEnd{0x1400E4B30, 0x140176A30};
|
||||
WEAK symbol<void(float, float, int)> Com_SetSlowMotion{0, 0x1400DB790};
|
||||
WEAK symbol<void(errorParm code, const char* message, ...)> Com_Error{0x1403509C0, 0x1400D78A0};
|
||||
WEAK symbol<void()> Com_Quit_f{0x140352BE0, 0x1400DA830};
|
||||
@ -74,7 +75,7 @@ namespace game
|
||||
WEAK symbol<void()> G_Glass_Update{0, 0x14033A640};
|
||||
WEAK symbol<int(int clientNum)> G_GetClientScore{0, 0x140342F90};
|
||||
|
||||
WEAK symbol<char* (char* string)> I_CleanStr{0x1403CD230, 0x140503D00};
|
||||
WEAK symbol<char*(char* string)> I_CleanStr{0x1403CD230, 0x140503D00};
|
||||
|
||||
WEAK symbol<const char* (int, int, int)> Key_KeynumToString{0x140187CC0, 0x14024FE10};
|
||||
|
||||
@ -118,9 +119,9 @@ namespace game
|
||||
|
||||
WEAK symbol<int(XAssetType type)> DB_GetXAssetTypeSize{0x14019A3B0, 0x14028BE70};
|
||||
|
||||
WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0, 0x1404CD210};
|
||||
WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x14039D5F0, 0x1404CD210};
|
||||
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0, 0x1404C7320};
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x1404709C0, 0x1404C7320};
|
||||
|
||||
WEAK symbol<scr_string_t(const char* str)> SL_FindString{0x140314AF0, 0x14043B470};
|
||||
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x140314D90, 0x14043B840}; // H1MP
|
||||
@ -161,8 +162,8 @@ namespace game
|
||||
|
||||
WEAK symbol<const char* (const char*)> UI_GetMapDisplayName{0, 0x140408CC0};
|
||||
WEAK symbol<const char* (const char*)> UI_GetGameTypeDisplayName{0, 0x1404086A0};
|
||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0, 0x1404CFE60};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0, 0x1404D21A0};
|
||||
WEAK symbol<void(unsigned int localClientNum, const char** args)> UI_RunMenuScript{0x14039EFF0, 0x1404CFE60};
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font, float scale)> UI_TextWidth{0x1403A0F20, 0x1404D21A0};
|
||||
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x140350430, 0x14041C580};
|
||||
|
||||
@ -221,4 +222,19 @@ namespace game
|
||||
{
|
||||
WEAK symbol<gentity_s> g_entities{0x14550DD90, 0};
|
||||
}
|
||||
|
||||
namespace hks
|
||||
{
|
||||
WEAK symbol<lua_State*> lua_state{0x141E2C2F8, 0x1426D3D08};
|
||||
WEAK symbol<void(lua_State* s, const char* str, unsigned int l)> hksi_lua_pushlstring{0x14004DA90, 0x1400624F0};
|
||||
WEAK symbol<HksObject*(HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_getfield{0x14009C0A0, 0x14012C600};
|
||||
WEAK symbol<void(lua_State* s, const HksObject* tbl, const HksObject* key, const HksObject* val)> hks_obj_settable{0x14009D240, 0x14012D820};
|
||||
WEAK symbol<HksObject* (HksObject* result, lua_State* s, const HksObject* table, const HksObject* key)> hks_obj_gettable{0x14009C580, 0x14012CAE0};
|
||||
WEAK symbol<void(lua_State* s, int nargs, int nresults, const unsigned int* pc)> vm_call_internal{0x1400C87A0, 0x140159EB0};
|
||||
WEAK symbol<HashTable*(lua_State* s, unsigned int arraySize, unsigned int hashSize)> Hashtable_Create{0x14008B3B0, 0x14011B320};
|
||||
WEAK symbol<cclosure*(lua_State* s, lua_function function, int num_upvalues,
|
||||
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x14008B5D0, 0x14011B540};
|
||||
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x1400A64D0, 0x140136D30};
|
||||
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x14009EF10, 0x14012F610};
|
||||
}
|
||||
}
|
||||
|
161
src/client/game/ui_scripting/execution.cpp
Normal file
161
src/client/game/ui_scripting/execution.cpp
Normal file
@ -0,0 +1,161 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
#include "component/ui_scripting.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
void push_value(const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
const auto value_ = value.get_raw();
|
||||
*state->m_apistack.top = value_;
|
||||
state->m_apistack.top++;
|
||||
}
|
||||
|
||||
script_value get_return_value(int offset)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
return state->m_apistack.top[-1 - offset];
|
||||
}
|
||||
|
||||
arguments get_return_values(int count)
|
||||
{
|
||||
arguments values;
|
||||
|
||||
for (auto i = count - 1; i >= 0; i--)
|
||||
{
|
||||
values.push_back(get_return_value(i));
|
||||
}
|
||||
|
||||
if (values.size() == 0)
|
||||
{
|
||||
values.push_back({});
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(function);
|
||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||
return get_return_values(count);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(key);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TUSERDATA;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||
return value;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
script_value get_field(const table& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(key);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject value{};
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TTABLE;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||
return value;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error getting table field: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TUSERDATA;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void set_field(const table& self, const script_value& key, const script_value& value)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
game::hks::HksObject userdata{};
|
||||
userdata.t = game::hks::TTABLE;
|
||||
userdata.v.ptr = self.ptr;
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error setting table field: ") + e.what());
|
||||
}
|
||||
}
|
||||
}
|
18
src/client/game/ui_scripting/execution.hpp
Normal file
18
src/client/game/ui_scripting/execution.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
void push_value(const script_value& value);
|
||||
script_value get_return_value(int offset);
|
||||
arguments get_return_values(int count);
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments);
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key);
|
||||
script_value get_field(const table& self, const script_value& key);
|
||||
void set_field(const userdata& self, const script_value& key, const script_value& value);
|
||||
void set_field(const table& self, const script_value& key, const script_value& value);
|
||||
}
|
205
src/client/game/ui_scripting/lua/context.cpp
Normal file
205
src/client/game/ui_scripting/lua/context.cpp
Normal file
@ -0,0 +1,205 @@
|
||||
#include <std_include.hpp>
|
||||
#include "context.hpp"
|
||||
#include "error.hpp"
|
||||
#include "value_conversion.hpp"
|
||||
#include "../script_value.hpp"
|
||||
#include "../execution.hpp"
|
||||
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
#include "../../../component/command.hpp"
|
||||
|
||||
#include "component/game_console.hpp"
|
||||
#include "component/scheduler.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
void setup_types(sol::state& state, scheduler& scheduler)
|
||||
{
|
||||
struct game
|
||||
{
|
||||
};
|
||||
auto game_type = state.new_usertype<game>("game_");
|
||||
state["game"] = game();
|
||||
|
||||
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||
const long long milliseconds)
|
||||
{
|
||||
return scheduler.add(callback, milliseconds, true);
|
||||
};
|
||||
|
||||
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
||||
const long long milliseconds)
|
||||
{
|
||||
return scheduler.add(callback, milliseconds, false);
|
||||
};
|
||||
|
||||
auto userdata_type = state.new_usertype<userdata>("userdata_");
|
||||
|
||||
userdata_type["new"] = sol::property(
|
||||
[](const userdata& userdata, const sol::this_state s)
|
||||
{
|
||||
return convert(s, userdata.get("new"));
|
||||
},
|
||||
[](const userdata& userdata, const sol::this_state s, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set("new", convert({s, value}));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
userdata_type["get"] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, userdata.get(convert({s, key})));
|
||||
};
|
||||
|
||||
userdata_type["set"] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, userdata.get(convert({s, key})));
|
||||
};
|
||||
|
||||
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto table_type = state.new_usertype<table>("table_");
|
||||
|
||||
table_type["new"] = sol::property(
|
||||
[](const table& table, const sol::this_state s)
|
||||
{
|
||||
return convert(s, table.get("new"));
|
||||
},
|
||||
[](const table& table, const sol::this_state s, const sol::lua_value& value)
|
||||
{
|
||||
table.set("new", convert({s, value}));
|
||||
}
|
||||
);
|
||||
|
||||
table_type["get"] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type["set"] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto function_type = state.new_usertype<function>("function_");
|
||||
|
||||
function_type[sol::meta_function::call] = [](const function& function, const sol::this_state s, sol::variadic_args va)
|
||||
{
|
||||
arguments arguments{};
|
||||
|
||||
for (auto arg : va)
|
||||
{
|
||||
arguments.push_back(convert({s, arg}));
|
||||
}
|
||||
|
||||
const auto values = function.call(arguments);
|
||||
std::vector<sol::lua_value> returns;
|
||||
|
||||
for (const auto& value : values)
|
||||
{
|
||||
returns.push_back(convert(s, value));
|
||||
}
|
||||
|
||||
return sol::as_returns(returns);
|
||||
};
|
||||
|
||||
state["luiglobals"] = table((*::game::hks::lua_state)->globals.v.table);
|
||||
state["CoD"] = state["luiglobals"]["CoD"];
|
||||
state["LUI"] = state["luiglobals"]["LUI"];
|
||||
state["Engine"] = state["luiglobals"]["Engine"];
|
||||
state["Game"] = state["luiglobals"]["Game"];
|
||||
}
|
||||
}
|
||||
|
||||
context::context(std::string folder)
|
||||
: folder_(std::move(folder))
|
||||
, scheduler_(state_)
|
||||
{
|
||||
this->state_.open_libraries(sol::lib::base,
|
||||
sol::lib::package,
|
||||
sol::lib::io,
|
||||
sol::lib::string,
|
||||
sol::lib::os,
|
||||
sol::lib::math,
|
||||
sol::lib::table);
|
||||
|
||||
this->state_["include"] = [this](const std::string& file)
|
||||
{
|
||||
this->load_script(file);
|
||||
};
|
||||
|
||||
sol::function old_require = this->state_["require"];
|
||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
||||
{
|
||||
return old_require(base_path + path);
|
||||
};
|
||||
|
||||
this->state_["scriptdir"] = [this]()
|
||||
{
|
||||
return this->folder_;
|
||||
};
|
||||
|
||||
setup_types(this->state_, this->scheduler_);
|
||||
|
||||
printf("Loading ui script '%s'\n", this->folder_.data());
|
||||
this->load_script("__init__");
|
||||
}
|
||||
|
||||
context::~context()
|
||||
{
|
||||
this->state_.collect_garbage();
|
||||
this->scheduler_.clear();
|
||||
this->state_ = {};
|
||||
}
|
||||
|
||||
void context::run_frame()
|
||||
{
|
||||
this->scheduler_.run_frame();
|
||||
this->state_.collect_garbage();
|
||||
}
|
||||
|
||||
void context::load_script(const std::string& script)
|
||||
{
|
||||
if (!this->loaded_scripts_.emplace(script).second)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
||||
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
||||
}
|
||||
}
|
36
src/client/game/ui_scripting/lua/context.hpp
Normal file
36
src/client/game/ui_scripting/lua/context.hpp
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4702)
|
||||
|
||||
#define SOL_ALL_SAFETIES_ON 1
|
||||
#define SOL_PRINT_ERRORS 0
|
||||
#include <sol/sol.hpp>
|
||||
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
class context
|
||||
{
|
||||
public:
|
||||
context(std::string folder);
|
||||
~context();
|
||||
|
||||
context(context&&) noexcept = delete;
|
||||
context& operator=(context&&) noexcept = delete;
|
||||
|
||||
context(const context&) = delete;
|
||||
context& operator=(const context&) = delete;
|
||||
|
||||
void run_frame();
|
||||
|
||||
private:
|
||||
sol::state state_{};
|
||||
std::string folder_;
|
||||
std::unordered_set<std::string> loaded_scripts_;
|
||||
|
||||
scheduler scheduler_;
|
||||
|
||||
void load_script(const std::string& script);
|
||||
};
|
||||
}
|
61
src/client/game/ui_scripting/lua/engine.cpp
Normal file
61
src/client/game/ui_scripting/lua/engine.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include <std_include.hpp>
|
||||
#include "engine.hpp"
|
||||
#include "context.hpp"
|
||||
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
#include "../../../component/game_module.hpp"
|
||||
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace ui_scripting::lua::engine
|
||||
{
|
||||
namespace
|
||||
{
|
||||
auto& get_scripts()
|
||||
{
|
||||
static std::vector<std::unique_ptr<context>> scripts{};
|
||||
return scripts;
|
||||
}
|
||||
|
||||
void load_scripts(const std::string& script_dir)
|
||||
{
|
||||
if (!utils::io::directory_exists(script_dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto scripts = utils::io::list_files(script_dir);
|
||||
|
||||
for (const auto& script : scripts)
|
||||
{
|
||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
||||
{
|
||||
get_scripts().push_back(std::make_unique<context>(script));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void start()
|
||||
{
|
||||
clear_converted_functions();
|
||||
get_scripts().clear();
|
||||
load_scripts(game_module::get_host_module().get_folder() + "/data/ui_scripts/");
|
||||
load_scripts("h1-mod/ui_scripts/");
|
||||
load_scripts("data/ui_scripts/");
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
clear_converted_functions();
|
||||
get_scripts().clear();
|
||||
}
|
||||
|
||||
void run_frame()
|
||||
{
|
||||
for (auto& script : get_scripts())
|
||||
{
|
||||
script->run_frame();
|
||||
}
|
||||
}
|
||||
}
|
8
src/client/game/ui_scripting/lua/engine.hpp
Normal file
8
src/client/game/ui_scripting/lua/engine.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
namespace ui_scripting::lua::engine
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
void run_frame();
|
||||
}
|
18
src/client/game/ui_scripting/lua/error.cpp
Normal file
18
src/client/game/ui_scripting/lua/error.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <std_include.hpp>
|
||||
#include "error.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
void handle_error(const sol::protected_function_result& result)
|
||||
{
|
||||
if (!result.valid())
|
||||
{
|
||||
printf("************** UI Script execution error **************\n");
|
||||
|
||||
const sol::error err = result;
|
||||
printf("%s\n", err.what());
|
||||
|
||||
printf("****************************************************\n");
|
||||
}
|
||||
}
|
||||
}
|
8
src/client/game/ui_scripting/lua/error.hpp
Normal file
8
src/client/game/ui_scripting/lua/error.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "context.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
void handle_error(const sol::protected_function_result& result);
|
||||
}
|
122
src/client/game/ui_scripting/lua/scheduler.cpp
Normal file
122
src/client/game/ui_scripting/lua/scheduler.cpp
Normal file
@ -0,0 +1,122 @@
|
||||
#include "std_include.hpp"
|
||||
#include "context.hpp"
|
||||
#include "error.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
scheduler::scheduler(sol::state& state)
|
||||
{
|
||||
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
||||
|
||||
task_handle_type["clear"] = [this](const task_handle& handle)
|
||||
{
|
||||
this->remove(handle);
|
||||
};
|
||||
}
|
||||
|
||||
void scheduler::run_frame()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
this->merge_callbacks();
|
||||
|
||||
for (auto i = tasks.begin(); i != tasks.end();)
|
||||
{
|
||||
const auto now = std::chrono::high_resolution_clock::now();
|
||||
const auto diff = now - i->last_call;
|
||||
|
||||
if (diff < i->delay)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
i->last_call = now;
|
||||
|
||||
if (!i->is_deleted)
|
||||
{
|
||||
handle_error(i->callback());
|
||||
}
|
||||
|
||||
if (i->is_volatile || i->is_deleted)
|
||||
{
|
||||
i = tasks.erase(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void scheduler::clear()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
new_tasks.clear();
|
||||
tasks.clear();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
||||
const bool is_volatile)
|
||||
{
|
||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
||||
}
|
||||
|
||||
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
||||
const bool is_volatile)
|
||||
{
|
||||
const uint64_t id = ++this->current_task_id_;
|
||||
|
||||
task task;
|
||||
task.is_volatile = is_volatile;
|
||||
task.callback = callback;
|
||||
task.delay = delay;
|
||||
task.last_call = std::chrono::steady_clock::now();
|
||||
task.id = id;
|
||||
task.is_deleted = false;
|
||||
|
||||
new_callbacks_.access([&task](task_list& tasks)
|
||||
{
|
||||
tasks.emplace_back(std::move(task));
|
||||
});
|
||||
|
||||
return {id};
|
||||
}
|
||||
|
||||
void scheduler::remove(const task_handle& handle)
|
||||
{
|
||||
auto mask_as_deleted = [&](task_list& tasks)
|
||||
{
|
||||
for (auto& task : tasks)
|
||||
{
|
||||
if (task.id == handle.id)
|
||||
{
|
||||
task.is_deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
callbacks_.access(mask_as_deleted);
|
||||
new_callbacks_.access(mask_as_deleted);
|
||||
}
|
||||
|
||||
void scheduler::merge_callbacks()
|
||||
{
|
||||
callbacks_.access([&](task_list& tasks)
|
||||
{
|
||||
new_callbacks_.access([&](task_list& new_tasks)
|
||||
{
|
||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||
new_tasks = {};
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
50
src/client/game/ui_scripting/lua/scheduler.hpp
Normal file
50
src/client/game/ui_scripting/lua/scheduler.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
#include <utils/concurrency.hpp>
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
class context;
|
||||
|
||||
class task_handle
|
||||
{
|
||||
public:
|
||||
uint64_t id = 0;
|
||||
};
|
||||
|
||||
class task final : public task_handle
|
||||
{
|
||||
public:
|
||||
std::chrono::steady_clock::time_point last_call{};
|
||||
sol::protected_function callback{};
|
||||
std::chrono::milliseconds delay{};
|
||||
bool is_volatile = false;
|
||||
bool is_deleted = false;
|
||||
};
|
||||
|
||||
class scheduler final
|
||||
{
|
||||
public:
|
||||
scheduler(sol::state& state);
|
||||
|
||||
scheduler(scheduler&&) noexcept = delete;
|
||||
scheduler& operator=(scheduler&&) noexcept = delete;
|
||||
|
||||
scheduler(const scheduler&) = delete;
|
||||
scheduler& operator=(const scheduler&) = delete;
|
||||
|
||||
void run_frame();
|
||||
void clear();
|
||||
|
||||
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
||||
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
||||
|
||||
private:
|
||||
using task_list = std::vector<task>;
|
||||
utils::concurrency::container<task_list> new_callbacks_;
|
||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||
std::atomic_int64_t current_task_id_ = 0;
|
||||
|
||||
void remove(const task_handle& handle);
|
||||
void merge_callbacks();
|
||||
};
|
||||
}
|
144
src/client/game/ui_scripting/lua/value_conversion.cpp
Normal file
144
src/client/game/ui_scripting/lua/value_conversion.cpp
Normal file
@ -0,0 +1,144 @@
|
||||
#include <std_include.hpp>
|
||||
#include "value_conversion.hpp"
|
||||
#include "../execution.hpp"
|
||||
#include "../../../component/ui_scripting.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
namespace
|
||||
{
|
||||
table convert_table(const sol::table& t)
|
||||
{
|
||||
table res{};
|
||||
|
||||
t.for_each([res](const sol::object& key, const sol::object& value)
|
||||
{
|
||||
res.set(convert(key), convert(value));
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
script_value convert_function(const sol::protected_function& function)
|
||||
{
|
||||
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
|
||||
add_converted_function(closure, function);
|
||||
|
||||
game::hks::HksObject value{};
|
||||
value.t = game::hks::TCFUNCTION;
|
||||
value.v.cClosure = closure;
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
script_value convert(const sol::lua_value& value)
|
||||
{
|
||||
if (value.is<bool>())
|
||||
{
|
||||
return {value.as<bool>()};
|
||||
}
|
||||
|
||||
if (value.is<int>())
|
||||
{
|
||||
return {value.as<int>()};
|
||||
}
|
||||
|
||||
if (value.is<unsigned int>())
|
||||
{
|
||||
return {value.as<unsigned int>()};
|
||||
}
|
||||
|
||||
if (value.is<double>())
|
||||
{
|
||||
return {value.as<double>()};
|
||||
}
|
||||
|
||||
if (value.is<float>())
|
||||
{
|
||||
return {value.as<float>()};
|
||||
}
|
||||
|
||||
if (value.is<std::string>())
|
||||
{
|
||||
return {value.as<std::string>()};
|
||||
}
|
||||
|
||||
if (value.is<lightuserdata>())
|
||||
{
|
||||
return {value.as<lightuserdata>()};
|
||||
}
|
||||
|
||||
if (value.is<userdata>())
|
||||
{
|
||||
return {value.as<userdata>()};
|
||||
}
|
||||
|
||||
if (value.is<table>())
|
||||
{
|
||||
return {value.as<table>()};
|
||||
}
|
||||
|
||||
if (value.is<function>())
|
||||
{
|
||||
return {value.as<function>()};
|
||||
}
|
||||
|
||||
if (value.is<sol::table>())
|
||||
{
|
||||
return {convert_table(value.as<sol::table>())};
|
||||
}
|
||||
|
||||
if (value.is<sol::protected_function>())
|
||||
{
|
||||
return {convert_function(value.as<sol::protected_function>())};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
sol::lua_value convert(lua_State* state, const script_value& value)
|
||||
{
|
||||
if (value.is<int>())
|
||||
{
|
||||
return {state, value.as<int>()};
|
||||
}
|
||||
|
||||
if (value.is<float>())
|
||||
{
|
||||
return {state, value.as<float>()};
|
||||
}
|
||||
|
||||
if (value.is<bool>())
|
||||
{
|
||||
return {state, value.as<bool>()};
|
||||
}
|
||||
|
||||
if (value.is<std::string>())
|
||||
{
|
||||
return {state, value.as<std::string>()};
|
||||
}
|
||||
|
||||
if (value.is<lightuserdata>())
|
||||
{
|
||||
return {state, value.as<lightuserdata>()};
|
||||
}
|
||||
|
||||
if (value.is<userdata>())
|
||||
{
|
||||
return {state, value.as<userdata>()};
|
||||
}
|
||||
|
||||
if (value.is<table>())
|
||||
{
|
||||
return {state, value.as<table>()};
|
||||
}
|
||||
|
||||
if (value.is<function>())
|
||||
{
|
||||
return {state, value.as<function>()};
|
||||
}
|
||||
|
||||
return {state, sol::lua_nil};
|
||||
}
|
||||
}
|
9
src/client/game/ui_scripting/lua/value_conversion.hpp
Normal file
9
src/client/game/ui_scripting/lua/value_conversion.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
#include "context.hpp"
|
||||
#include "../script_value.hpp"
|
||||
|
||||
namespace ui_scripting::lua
|
||||
{
|
||||
script_value convert(const sol::lua_value& value);
|
||||
sol::lua_value convert(lua_State* state, const script_value& value);
|
||||
}
|
274
src/client/game/ui_scripting/script_value.cpp
Normal file
274
src/client/game/ui_scripting/script_value.cpp
Normal file
@ -0,0 +1,274 @@
|
||||
#include <std_include.hpp>
|
||||
#include "execution.hpp"
|
||||
#include "types.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
/***************************************************************
|
||||
* Constructors
|
||||
**************************************************************/
|
||||
|
||||
script_value::script_value(const game::hks::HksObject& value)
|
||||
: value_(value)
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const int value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const unsigned int value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const bool value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TBOOLEAN;
|
||||
obj.v.boolean = value;
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const float value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
obj.t = game::hks::TNUMBER;
|
||||
obj.v.number = static_cast<float>(value);
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const double value)
|
||||
: script_value(static_cast<float>(value))
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const char* value)
|
||||
{
|
||||
game::hks::HksObject obj{};
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
game::hks::hksi_lua_pushlstring(state, value, (unsigned int)strlen(value));
|
||||
obj = state->m_apistack.top[-1];
|
||||
|
||||
this->value_ = obj;
|
||||
}
|
||||
|
||||
script_value::script_value(const std::string& value)
|
||||
: script_value(value.data())
|
||||
{
|
||||
}
|
||||
|
||||
script_value::script_value(const lightuserdata& value)
|
||||
{
|
||||
this->value_.t = game::hks::TLIGHTUSERDATA;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
}
|
||||
|
||||
script_value::script_value(const userdata& value)
|
||||
{
|
||||
this->value_.t = game::hks::TUSERDATA;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
}
|
||||
|
||||
script_value::script_value(const table& value)
|
||||
{
|
||||
this->value_.t = game::hks::TTABLE;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
}
|
||||
|
||||
script_value::script_value(const function& value)
|
||||
{
|
||||
this->value_.t = value.type;
|
||||
this->value_.v.ptr = value.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Integer
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<int>() const
|
||||
{
|
||||
const auto number = this->get_raw().v.number;
|
||||
return this->get_raw().t == game::hks::TNUMBER && static_cast<int>(number) == number;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<unsigned int>() const
|
||||
{
|
||||
return this->is<int>();
|
||||
}
|
||||
|
||||
template <>
|
||||
int script_value::get() const
|
||||
{
|
||||
return static_cast<int>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
template <>
|
||||
unsigned int script_value::get() const
|
||||
{
|
||||
return static_cast<unsigned int>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Boolean
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<bool>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TBOOLEAN;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.boolean;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Float
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<float>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TNUMBER;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<double>() const
|
||||
{
|
||||
return this->is<float>();
|
||||
}
|
||||
|
||||
template <>
|
||||
float script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.number;
|
||||
}
|
||||
|
||||
template <>
|
||||
double script_value::get() const
|
||||
{
|
||||
return static_cast<double>(this->get_raw().v.number);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* String
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<const char*>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TSTRING;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool script_value::is<std::string>() const
|
||||
{
|
||||
return this->is<const char*>();
|
||||
}
|
||||
|
||||
template <>
|
||||
const char* script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.str->m_data;
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string script_value::get() const
|
||||
{
|
||||
return this->get<const char*>();
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<lightuserdata>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TLIGHTUSERDATA;
|
||||
}
|
||||
|
||||
template <>
|
||||
lightuserdata script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<userdata>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TUSERDATA;
|
||||
}
|
||||
|
||||
template <>
|
||||
userdata script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.ptr;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<table>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TTABLE;
|
||||
}
|
||||
|
||||
template <>
|
||||
table script_value::get() const
|
||||
{
|
||||
return this->get_raw().v.table;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
template <>
|
||||
bool script_value::is<function>() const
|
||||
{
|
||||
return this->get_raw().t == game::hks::TIFUNCTION
|
||||
|| this->get_raw().t == game::hks::TCFUNCTION;
|
||||
}
|
||||
|
||||
template <>
|
||||
function script_value::get() const
|
||||
{
|
||||
return { this->get_raw().v.cClosure, this->get_raw().t };
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
**************************************************************/
|
||||
|
||||
const game::hks::HksObject& script_value::get_raw() const
|
||||
{
|
||||
return this->value_;
|
||||
}
|
||||
}
|
56
src/client/game/ui_scripting/script_value.hpp
Normal file
56
src/client/game/ui_scripting/script_value.hpp
Normal file
@ -0,0 +1,56 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata;
|
||||
class userdata;
|
||||
class table;
|
||||
class function;
|
||||
|
||||
class script_value
|
||||
{
|
||||
public:
|
||||
script_value() = default;
|
||||
script_value(const game::hks::HksObject& value);
|
||||
|
||||
script_value(int value);
|
||||
script_value(unsigned int value);
|
||||
script_value(bool value);
|
||||
|
||||
script_value(float value);
|
||||
script_value(double value);
|
||||
|
||||
script_value(const char* value);
|
||||
script_value(const std::string& value);
|
||||
|
||||
script_value(const lightuserdata& value);
|
||||
script_value(const userdata& value);
|
||||
script_value(const table& value);
|
||||
script_value(const function& value);
|
||||
|
||||
template <typename T>
|
||||
bool is() const;
|
||||
|
||||
template <typename T>
|
||||
T as() const
|
||||
{
|
||||
if (!this->is<T>())
|
||||
{
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
|
||||
return get<T>();
|
||||
}
|
||||
|
||||
const game::hks::HksObject& get_raw() const;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T get() const;
|
||||
|
||||
game::hks::HksObject value_{};
|
||||
};
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
}
|
276
src/client/game/ui_scripting/types.cpp
Normal file
276
src/client/game/ui_scripting/types.cpp
Normal file
@ -0,0 +1,276 @@
|
||||
#include <std_include.hpp>
|
||||
#include "types.hpp"
|
||||
#include "execution.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
/***************************************************************
|
||||
* Lightuserdata
|
||||
**************************************************************/
|
||||
|
||||
lightuserdata::lightuserdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Userdata
|
||||
**************************************************************/
|
||||
|
||||
userdata::userdata(void* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
userdata::userdata(const userdata& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
userdata::userdata(userdata&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
userdata::~userdata()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(const userdata& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
userdata& userdata::operator=(userdata&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void userdata::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.ptr = this->ptr;
|
||||
value.t = game::hks::TUSERDATA;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void userdata::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void userdata::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value userdata::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Table
|
||||
**************************************************************/
|
||||
|
||||
table::table()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
this->ptr = game::hks::Hashtable_Create(state, 0, 0);
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(game::hks::HashTable* ptr_)
|
||||
: ptr(ptr_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
table::table(const table& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
table::table(table&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
table::~table()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
table& table::operator=(const table& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
table& table::operator=(table&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void table::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.table = this->ptr;
|
||||
value.t = game::hks::TTABLE;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void table::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
void table::set(const script_value& key, const script_value& value) const
|
||||
{
|
||||
set_field(*this, key, value);
|
||||
}
|
||||
|
||||
script_value table::get(const script_value& key) const
|
||||
{
|
||||
return get_field(*this, key);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Function
|
||||
**************************************************************/
|
||||
|
||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||
: ptr(ptr_)
|
||||
, type(type_)
|
||||
{
|
||||
this->add();
|
||||
}
|
||||
|
||||
function::function(const function& other)
|
||||
{
|
||||
this->operator=(other);
|
||||
}
|
||||
|
||||
function::function(function&& other) noexcept
|
||||
{
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
function::~function()
|
||||
{
|
||||
this->release();
|
||||
}
|
||||
|
||||
function& function::operator=(const function& other)
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
this->add();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
function& function::operator=(function&& other) noexcept
|
||||
{
|
||||
if (&other != this)
|
||||
{
|
||||
this->release();
|
||||
this->ptr = other.ptr;
|
||||
this->type = other.type;
|
||||
this->ref = other.ref;
|
||||
other.ref = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void function::add()
|
||||
{
|
||||
game::hks::HksObject value{};
|
||||
value.v.cClosure = this->ptr;
|
||||
value.t = this->type;
|
||||
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
push_value(value);
|
||||
|
||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||
}
|
||||
|
||||
void function::release()
|
||||
{
|
||||
if (this->ref)
|
||||
{
|
||||
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref);
|
||||
}
|
||||
}
|
||||
|
||||
arguments function::call(const arguments& arguments) const
|
||||
{
|
||||
return call_script_function(*this, arguments);
|
||||
}
|
||||
}
|
89
src/client/game/ui_scripting/types.hpp
Normal file
89
src/client/game/ui_scripting/types.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
#include "game/game.hpp"
|
||||
#include "script_value.hpp"
|
||||
|
||||
namespace ui_scripting
|
||||
{
|
||||
class lightuserdata
|
||||
{
|
||||
public:
|
||||
lightuserdata(void*);
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
class userdata
|
||||
{
|
||||
public:
|
||||
userdata(void*);
|
||||
|
||||
userdata(const userdata& other);
|
||||
userdata(userdata&& other) noexcept;
|
||||
|
||||
~userdata();
|
||||
|
||||
userdata& operator=(const userdata& other);
|
||||
userdata& operator=(userdata&& other) noexcept;
|
||||
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
void* ptr;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
|
||||
class table
|
||||
{
|
||||
public:
|
||||
table();
|
||||
table(game::hks::HashTable* ptr_);
|
||||
|
||||
table(const table& other);
|
||||
table(table&& other) noexcept;
|
||||
|
||||
~table();
|
||||
|
||||
table& operator=(const table& other);
|
||||
table& operator=(table&& other) noexcept;
|
||||
|
||||
script_value get(const script_value& key) const;
|
||||
void set(const script_value& key, const script_value& value) const;
|
||||
|
||||
game::hks::HashTable* ptr;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
|
||||
class function
|
||||
{
|
||||
public:
|
||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||
|
||||
function(const function& other);
|
||||
function(function&& other) noexcept;
|
||||
|
||||
~function();
|
||||
|
||||
function& operator=(const function& other);
|
||||
function& operator=(function&& other) noexcept;
|
||||
|
||||
arguments call(const arguments& arguments) const;
|
||||
|
||||
game::hks::cclosure* ptr;
|
||||
game::hks::HksObjectType type;
|
||||
|
||||
private:
|
||||
void add();
|
||||
void release();
|
||||
|
||||
int ref{};
|
||||
};
|
||||
}
|
@ -97,7 +97,7 @@ FARPROC load_binary(const launcher::mode mode)
|
||||
if (!utils::io::read_file(binary, &data))
|
||||
{
|
||||
throw std::runtime_error(utils::string::va(
|
||||
"Failed to read game binary (%s)!\nPlease copy the h1x.exe into your Call of Duty: Modern Warfare Remastered installation folder and run it from there.",
|
||||
"Failed to read game binary (%s)!\nPlease copy the h1-mod.exe into your Call of Duty: Modern Warfare Remastered installation folder and run it from there.",
|
||||
binary.data()));
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma warning(disable: 4702)
|
||||
#pragma warning(disable: 4996)
|
||||
#pragma warning(disable: 5054)
|
||||
#pragma warning(disable: 5056)
|
||||
#pragma warning(disable: 6011)
|
||||
#pragma warning(disable: 6297)
|
||||
#pragma warning(disable: 6385)
|
||||
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user