Big LUI scripting changes
This commit is contained in:
parent
3be2ae0ed9
commit
35cdd3927e
@ -2,12 +2,12 @@ game:addlocalizedstring("MENU_MODS", "MODS")
|
|||||||
game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.")
|
game:addlocalizedstring("MENU_MODS_DESC", "Load installed mods.")
|
||||||
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
|
game:addlocalizedstring("LUA_MENU_MOD_DESC_DEFAULT", "Load &&1.")
|
||||||
game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3")
|
game:addlocalizedstring("LUA_MENU_MOD_DESC", "&&1\nAuthor: &&2\nVersion: &&3")
|
||||||
game:addlocalizedstring("LUA_MENU_OPEN_STORE", "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_LOADED_MOD", "Loaded mod: ^3&&1")
|
||||||
game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods")
|
game:addlocalizedstring("LUA_MENU_AVAILABLE_MODS", "Available mods")
|
||||||
game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload")
|
game:addlocalizedstring("LUA_MENU_UNLOAD", "Unload")
|
||||||
game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.")
|
game:addlocalizedstring("LUA_MENU_UNLOAD_DESC", "Unload the currently loaded mod.")
|
||||||
|
game:addlocalizedstring("LUA_MENU_WORKSHOP", "Workshop")
|
||||||
|
game:addlocalizedstring("LUA_MENU_WORKSHOP_DESC", "Download and install mods.")
|
||||||
|
|
||||||
function createdivider(menu, text)
|
function createdivider(menu, text)
|
||||||
local element = LUI.UIElement.new( {
|
local element = LUI.UIElement.new( {
|
||||||
@ -26,6 +26,8 @@ function createdivider(menu, text)
|
|||||||
title_bar_text = Engine.ToUpperCase(text)
|
title_bar_text = Engine.ToUpperCase(text)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
element.text = element:getFirstChild():getFirstChild():getNextSibling()
|
||||||
|
|
||||||
menu.list:addElement(element)
|
menu.list:addElement(element)
|
||||||
return element
|
return element
|
||||||
end
|
end
|
||||||
@ -78,12 +80,12 @@ LUI.MenuBuilder.registerType("mods_menu", function(a1)
|
|||||||
showTopRightSmallBar = true
|
showTopRightSmallBar = true
|
||||||
})
|
})
|
||||||
|
|
||||||
menu:AddButton("@LUA_MENU_OPEN_STORE", function()
|
menu:AddButton("@LUA_MENU_WORKSHOP", function()
|
||||||
if (LUI.MenuBuilder.m_types_build["mods_store_menu"]) then
|
if (LUI.MenuBuilder.m_types_build["mods_workshop_menu"]) then
|
||||||
LUI.FlowManager.RequestAddMenu(nil, "mods_store_menu")
|
LUI.FlowManager.RequestAddMenu(nil, "mods_workshop_menu")
|
||||||
end
|
end
|
||||||
end, nil, true, nil, {
|
end, nil, true, nil, {
|
||||||
desc_text = Engine.Localize("@LUA_MENU_OPEN_STORE_DESC")
|
desc_text = Engine.Localize("@LUA_MENU_WORKSHOP_DESC")
|
||||||
})
|
})
|
||||||
|
|
||||||
local modfolder = game:getloadedmod()
|
local modfolder = game:getloadedmod()
|
||||||
@ -92,7 +94,7 @@ LUI.MenuBuilder.registerType("mods_menu", function(a1)
|
|||||||
createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", name:truncate(24)))
|
createdivider(menu, Engine.Localize("@LUA_MENU_LOADED_MOD", name:truncate(24)))
|
||||||
|
|
||||||
menu:AddButton("@LUA_MENU_UNLOAD", function()
|
menu:AddButton("@LUA_MENU_UNLOAD", function()
|
||||||
game:executecommand("unloadmod")
|
Engine.Exec("unloadmod")
|
||||||
end, nil, true, nil, {
|
end, nil, true, nil, {
|
||||||
desc_text = Engine.Localize("@LUA_MENU_UNLOAD_DESC")
|
desc_text = Engine.Localize("@LUA_MENU_UNLOAD_DESC")
|
||||||
})
|
})
|
||||||
@ -109,7 +111,7 @@ LUI.MenuBuilder.registerType("mods_menu", function(a1)
|
|||||||
if (mods[i] ~= modfolder) then
|
if (mods[i] ~= modfolder) then
|
||||||
game:addlocalizedstring(name, name)
|
game:addlocalizedstring(name, name)
|
||||||
menu:AddButton(name, function()
|
menu:AddButton(name, function()
|
||||||
game:executecommand("loadmod " .. mods[i])
|
Engine.Exec("loadmod " .. mods[i])
|
||||||
end, nil, true, nil, {
|
end, nil, true, nil, {
|
||||||
desc_text = desc
|
desc_text = desc
|
||||||
})
|
})
|
||||||
|
@ -55,8 +55,8 @@ LUI.addmenubutton("pc_controls", {
|
|||||||
LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
|
LUI.MenuBuilder.m_types_build["settings_menu"] = function(a1)
|
||||||
local menu = LUI.MenuTemplate.new(a1, {
|
local menu = LUI.MenuTemplate.new(a1, {
|
||||||
menu_title = "@MENU_GENERAL",
|
menu_title = "@MENU_GENERAL",
|
||||||
menu_list_divider_top_offset = -(LUI.H1MenuTab.tabChangeHoldingElementHeight + luiglobals.H1MenuDims.spacing),
|
menu_list_divider_top_offset = -(LUI.H1MenuTab.tabChangeHoldingElementHeight + H1MenuDims.spacing),
|
||||||
menu_width = luiglobals.GenericMenuDims.OptionMenuWidth
|
menu_width = GenericMenuDims.OptionMenuWidth
|
||||||
})
|
})
|
||||||
|
|
||||||
createdivider(menu, "@LUA_MENU_UPDATES")
|
createdivider(menu, "@LUA_MENU_UPDATES")
|
||||||
|
@ -27,13 +27,18 @@ namespace filesystem
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool read_file(const std::string& path, std::string* data)
|
bool read_file(const std::string& path, std::string* data, std::string* real_path)
|
||||||
{
|
{
|
||||||
for (const auto& search_path : get_search_paths())
|
for (const auto& search_path : get_search_paths())
|
||||||
{
|
{
|
||||||
const auto path_ = search_path + "/" + path;
|
const auto path_ = search_path + "/" + path;
|
||||||
if (utils::io::read_file(path_, data))
|
if (utils::io::read_file(path_, data))
|
||||||
{
|
{
|
||||||
|
if (real_path != nullptr)
|
||||||
|
{
|
||||||
|
*real_path = path_;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,5 @@ namespace filesystem
|
|||||||
{
|
{
|
||||||
std::unordered_set<std::string>& get_search_paths();
|
std::unordered_set<std::string>& get_search_paths();
|
||||||
std::string read_file(const std::string& path);
|
std::string read_file(const std::string& path);
|
||||||
bool read_file(const std::string& path, std::string* data);
|
bool read_file(const std::string& path, std::string* data, std::string* real_path = nullptr);
|
||||||
}
|
}
|
@ -386,11 +386,13 @@ namespace game_console
|
|||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
const auto formatted = std::string(va_buffer);
|
const auto formatted = std::string(va_buffer);
|
||||||
|
printf(va_buffer);
|
||||||
|
|
||||||
const auto lines = utils::string::split(formatted, '\n');
|
const auto lines = utils::string::split(formatted, '\n');
|
||||||
|
|
||||||
for (auto& line : lines)
|
for (auto& line : lines)
|
||||||
{
|
{
|
||||||
print(type == con_type_info ? line : "^"s.append(std::to_string(type)).append(line));
|
print(type == con_type_info ? line : "^"s.append(std::to_string(type)).append(line), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "game_console.hpp"
|
#include "game_console.hpp"
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "game/ui_scripting/lua/engine.hpp"
|
|
||||||
#include "game/ui_scripting/execution.hpp"
|
#include "game/ui_scripting/execution.hpp"
|
||||||
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
@ -87,6 +87,7 @@ namespace scheduler
|
|||||||
utils::hook::detour r_end_frame_hook;
|
utils::hook::detour r_end_frame_hook;
|
||||||
utils::hook::detour g_run_frame_hook;
|
utils::hook::detour g_run_frame_hook;
|
||||||
utils::hook::detour main_frame_hook;
|
utils::hook::detour main_frame_hook;
|
||||||
|
utils::hook::detour hks_frame_hook;
|
||||||
|
|
||||||
void execute(const pipeline type)
|
void execute(const pipeline type)
|
||||||
{
|
{
|
||||||
@ -97,11 +98,6 @@ namespace scheduler
|
|||||||
void r_end_frame_stub()
|
void r_end_frame_stub()
|
||||||
{
|
{
|
||||||
execute(pipeline::renderer);
|
execute(pipeline::renderer);
|
||||||
if (game::Sys_IsMainThread())
|
|
||||||
{
|
|
||||||
execute(pipeline::lui);
|
|
||||||
}
|
|
||||||
|
|
||||||
r_end_frame_hook.invoke<void>();
|
r_end_frame_hook.invoke<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +112,16 @@ namespace scheduler
|
|||||||
main_frame_hook.invoke<void>();
|
main_frame_hook.invoke<void>();
|
||||||
execute(pipeline::main);
|
execute(pipeline::main);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void hks_frame_stub()
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
execute(pipeline::lui);
|
||||||
|
}
|
||||||
|
hks_frame_hook.invoke<void>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void schedule(const std::function<bool()>& callback, const pipeline type,
|
void schedule(const std::function<bool()>& callback, const pipeline type,
|
||||||
@ -186,6 +192,7 @@ namespace scheduler
|
|||||||
r_end_frame_hook.create(0x14076D7B0, scheduler::r_end_frame_stub);
|
r_end_frame_hook.create(0x14076D7B0, scheduler::r_end_frame_stub);
|
||||||
g_run_frame_hook.create(0x1404CB030, scheduler::server_frame_stub);
|
g_run_frame_hook.create(0x1404CB030, scheduler::server_frame_stub);
|
||||||
main_frame_hook.create(0x140417FA0, scheduler::main_frame_stub);
|
main_frame_hook.create(0x140417FA0, scheduler::main_frame_stub);
|
||||||
|
hks_frame_hook.create(0x140327880, scheduler::hks_frame_stub);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pre_destroy() override
|
void pre_destroy() override
|
||||||
|
@ -7,172 +7,461 @@
|
|||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
|
||||||
#include "ui_scripting.hpp"
|
#include "filesystem.hpp"
|
||||||
|
#include "localized_strings.hpp"
|
||||||
|
#include "scripting.hpp"
|
||||||
|
#include "fastfiles.hpp"
|
||||||
|
#include "mods.hpp"
|
||||||
|
#include "updater.hpp"
|
||||||
|
|
||||||
#include "game/ui_scripting/lua/engine.hpp"
|
|
||||||
#include "game/ui_scripting/execution.hpp"
|
#include "game/ui_scripting/execution.hpp"
|
||||||
#include "game/ui_scripting/lua/error.hpp"
|
#include "game/scripting/execution.hpp"
|
||||||
|
|
||||||
|
#include "ui_scripting.hpp"
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
std::unordered_map<game::hks::cclosure*, sol::protected_function> converted_functions;
|
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
||||||
|
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
||||||
|
const auto lua_json = utils::nt::load_resource(LUA_JSON);
|
||||||
|
|
||||||
|
std::unordered_map<game::hks::cclosure*, std::function<arguments(const function_arguments& args)>> converted_functions;
|
||||||
|
|
||||||
utils::hook::detour hksi_lual_error_hook;
|
|
||||||
utils::hook::detour hksi_lual_error_hook2;
|
|
||||||
utils::hook::detour hks_start_hook;
|
utils::hook::detour hks_start_hook;
|
||||||
utils::hook::detour hks_shutdown_hook;
|
utils::hook::detour hks_shutdown_hook;
|
||||||
utils::hook::detour hks_allocator_hook;
|
utils::hook::detour hks_package_require_hook;
|
||||||
utils::hook::detour lui_error_hook;
|
|
||||||
utils::hook::detour hksi_hks_error_hook;
|
|
||||||
utils::hook::detour hks_frame_hook;
|
|
||||||
|
|
||||||
int error_hook_enabled = 0;
|
struct script
|
||||||
|
|
||||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
|
||||||
{
|
{
|
||||||
char va_buffer[2048] = {0};
|
std::string name;
|
||||||
|
std::string root;
|
||||||
|
};
|
||||||
|
|
||||||
va_list ap;
|
struct globals_t
|
||||||
va_start(ap, fmt);
|
{
|
||||||
vsprintf_s(va_buffer, fmt, ap);
|
std::string in_require_script;
|
||||||
va_end(ap);
|
std::vector<script> loaded_scripts;
|
||||||
|
bool load_raw_script{};
|
||||||
|
std::string raw_script_name{};
|
||||||
|
};
|
||||||
|
|
||||||
const auto formatted = std::string(va_buffer);
|
globals_t globals{};
|
||||||
|
|
||||||
if (!error_hook_enabled)
|
bool is_loaded_script(const std::string& name)
|
||||||
|
{
|
||||||
|
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
||||||
{
|
{
|
||||||
return hksi_lual_error_hook.invoke<void>(s, formatted.data());
|
if (i->name == name)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error(formatted);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hksi_hks_error_stub(game::hks::lua_State* s, int a2)
|
std::string get_root_script(const std::string& name)
|
||||||
{
|
{
|
||||||
if (!error_hook_enabled)
|
for (auto i = globals.loaded_scripts.begin(); i != globals.loaded_scripts.end(); ++i)
|
||||||
{
|
{
|
||||||
return hksi_hks_error_hook.invoke<void>(s, a2);
|
if (i->name == name)
|
||||||
|
{
|
||||||
|
return i->root;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw std::runtime_error("unknown error");
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void lui_error_stub(game::hks::lua_State* s)
|
table get_globals()
|
||||||
{
|
{
|
||||||
if (!error_hook_enabled)
|
const auto state = *game::hks::lua_state;
|
||||||
|
return state->globals.v.table;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_error(const std::string& error)
|
||||||
|
{
|
||||||
|
printf("************** UI Script execution error **************\n");
|
||||||
|
printf("%s\n", error.data());
|
||||||
|
printf("****************************************************\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_loading_script(const std::string& name)
|
||||||
|
{
|
||||||
|
printf("Loading LUI script '%s'\n", name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_current_script()
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
game::hks::lua_Debug info{};
|
||||||
|
game::hks::hksi_lua_getstack(state, 1, &info);
|
||||||
|
game::hks::hksi_lua_getinfo(state, "nSl", &info);
|
||||||
|
return info.short_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
int load_buffer(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto sharing_mode = state->m_global->m_bytecodeSharingMode;
|
||||||
|
state->m_global->m_bytecodeSharingMode = game::hks::HKS_BYTECODE_SHARING_ON;
|
||||||
|
const auto _0 = gsl::finally([&]()
|
||||||
{
|
{
|
||||||
return lui_error_hook.invoke<void>(s);
|
state->m_global->m_bytecodeSharingMode = sharing_mode;
|
||||||
|
});
|
||||||
|
|
||||||
|
game::hks::HksCompilerSettings compiler_settings{};
|
||||||
|
return game::hks::hksi_hksL_loadbuffer(state, &compiler_settings, data.data(), data.size(), name.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_script(const std::string& name, const std::string& data)
|
||||||
|
{
|
||||||
|
globals.loaded_scripts.push_back({name, name});
|
||||||
|
|
||||||
|
const auto lua = get_globals();
|
||||||
|
const auto load_results = lua["loadstring"](data, name);
|
||||||
|
|
||||||
|
if (load_results[0].is<function>())
|
||||||
|
{
|
||||||
|
const auto results = lua["pcall"](load_results);
|
||||||
|
if (!results[0].as<bool>())
|
||||||
|
{
|
||||||
|
print_error(results[1].as<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (load_results[1].is<std::string>())
|
||||||
|
{
|
||||||
|
print_error(load_results[1].as<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void load_scripts(const std::string& script_dir)
|
||||||
|
{
|
||||||
|
if (!utils::io::directory_exists(script_dir))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto count = static_cast<int>(s->m_apistack.top - s->m_apistack.base);
|
const auto scripts = utils::io::list_files(script_dir);
|
||||||
const auto arguments = get_return_values(count);
|
|
||||||
|
|
||||||
std::string error_str = "LUI Error";
|
for (const auto& script : scripts)
|
||||||
if (count && arguments[0].is<std::string>())
|
|
||||||
{
|
{
|
||||||
error_str = arguments[0].as<std::string>();
|
std::string data{};
|
||||||
|
if (std::filesystem::is_directory(script) && utils::io::read_file(script + "/__init__.lua", &data))
|
||||||
|
{
|
||||||
|
print_loading_script(script);
|
||||||
|
load_script(script + "/__init__.lua", data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw std::runtime_error(error_str);
|
void setup_functions()
|
||||||
|
{
|
||||||
|
const auto lua = get_globals();
|
||||||
|
|
||||||
|
lua["io"] = table();
|
||||||
|
lua["io"]["fileexists"] = utils::io::file_exists;
|
||||||
|
lua["io"]["writefile"] = utils::io::write_file;
|
||||||
|
lua["io"]["movefile"] = utils::io::move_file;
|
||||||
|
lua["io"]["filesize"] = utils::io::file_size;
|
||||||
|
lua["io"]["createdirectory"] = utils::io::create_directory;
|
||||||
|
lua["io"]["directoryexists"] = utils::io::directory_exists;
|
||||||
|
lua["io"]["directoryisempty"] = utils::io::directory_is_empty;
|
||||||
|
lua["io"]["listfiles"] = utils::io::list_files;
|
||||||
|
lua["io"]["removefile"] = utils::io::remove_file;
|
||||||
|
lua["io"]["removedirectory"] = utils::io::remove_directory;
|
||||||
|
lua["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
|
||||||
|
|
||||||
|
using game = table;
|
||||||
|
auto game_type = game();
|
||||||
|
lua["game"] = game_type;
|
||||||
|
|
||||||
|
game_type["addlocalizedstring"] = [](const game&, const std::string& a, const std::string& b)
|
||||||
|
{
|
||||||
|
localized_strings::override(a, b);
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
|
||||||
|
{
|
||||||
|
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
|
||||||
|
{
|
||||||
|
table[key] = value;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["sharedget"] = [](const game&, const std::string& key)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
|
||||||
|
{
|
||||||
|
result = table[key];
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["sharedclear"] = [](const game&)
|
||||||
|
{
|
||||||
|
scripting::shared_table.access([](scripting::shared_table_t& table)
|
||||||
|
{
|
||||||
|
table.clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["assetlist"] = [](const game&, const std::string& type_string)
|
||||||
|
{
|
||||||
|
auto table_ = table();
|
||||||
|
auto index = 1;
|
||||||
|
auto type_index = -1;
|
||||||
|
|
||||||
|
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
|
||||||
|
{
|
||||||
|
if (type_string == ::game::g_assetNames[i])
|
||||||
|
{
|
||||||
|
type_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type_index == -1)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Asset type does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto type = static_cast<::game::XAssetType>(type_index);
|
||||||
|
fastfiles::enum_assets(type, [type, &table_, &index](const ::game::XAssetHeader header)
|
||||||
|
{
|
||||||
|
const auto asset = ::game::XAsset{ type, header };
|
||||||
|
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
|
||||||
|
table_[index++] = asset_name;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
return table_;
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
|
||||||
|
{
|
||||||
|
const auto alternate = name.starts_with("alt_");
|
||||||
|
const auto weapon = ::game::G_GetWeaponForName(name.data());
|
||||||
|
|
||||||
|
char buffer[0x400] = { 0 };
|
||||||
|
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
|
||||||
|
|
||||||
|
return std::string(buffer);
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["getloadedmod"] = [](const game&)
|
||||||
|
{
|
||||||
|
return mods::mod_path;
|
||||||
|
};
|
||||||
|
|
||||||
|
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
|
||||||
|
const std::string& value)
|
||||||
|
{
|
||||||
|
localized_strings::override(string, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
using player = table;
|
||||||
|
auto player_type = player();
|
||||||
|
lua["player"] = player_type;
|
||||||
|
|
||||||
|
player_type["notify"] = [](const player&, const std::string& name, const variadic_args& va)
|
||||||
|
{
|
||||||
|
if (!::game::CL_IsCgameInitialized() || !::game::g_entities[0].client)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Not in game");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto to_string = get_globals()["tostring"];
|
||||||
|
const auto arguments = get_return_values();
|
||||||
|
std::vector<std::string> args{};
|
||||||
|
for (const auto& value : va)
|
||||||
|
{
|
||||||
|
args.push_back(to_string(value)[0].as<std::string>());
|
||||||
|
}
|
||||||
|
|
||||||
|
::scheduler::once([name, args]()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
std::vector<scripting::script_value> arguments{};
|
||||||
|
|
||||||
|
for (const auto& arg : args)
|
||||||
|
{
|
||||||
|
arguments.push_back(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
||||||
|
scripting::notify(player, name, arguments);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}, ::scheduler::pipeline::server);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto updater_table = table();
|
||||||
|
lua["updater"] = updater_table;
|
||||||
|
|
||||||
|
updater_table["relaunch"] = updater::relaunch;
|
||||||
|
|
||||||
|
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
|
||||||
|
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
|
||||||
|
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
|
||||||
|
|
||||||
|
updater_table["startupdatecheck"] = updater::start_update_check;
|
||||||
|
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
|
||||||
|
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
|
||||||
|
updater_table["isupdateavailable"] = updater::is_update_available;
|
||||||
|
|
||||||
|
updater_table["startupdatedownload"] = updater::start_update_download;
|
||||||
|
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
|
||||||
|
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
|
||||||
|
updater_table["cancelupdate"] = updater::cancel_update;
|
||||||
|
updater_table["isrestartrequired"] = updater::is_restart_required;
|
||||||
|
|
||||||
|
updater_table["getlasterror"] = updater::get_last_error;
|
||||||
|
updater_table["getcurrentfile"] = updater::get_current_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void start()
|
||||||
|
{
|
||||||
|
globals = {};
|
||||||
|
|
||||||
|
const auto lua = get_globals();
|
||||||
|
lua["EnableGlobals"]();
|
||||||
|
|
||||||
|
setup_functions();
|
||||||
|
|
||||||
|
lua["print"] = function(reinterpret_cast<game::hks::lua_function>(0x1402B81C0));
|
||||||
|
lua["table"]["unpack"] = lua["unpack"];
|
||||||
|
lua["luiglobals"] = lua;
|
||||||
|
|
||||||
|
load_script("lui_common", lui_common);
|
||||||
|
load_script("lui_updater", lui_updater);
|
||||||
|
load_script("lua_json", lua_json);
|
||||||
|
|
||||||
|
for (const auto& path : filesystem::get_search_paths())
|
||||||
|
{
|
||||||
|
load_scripts(path + "/ui_scripts/");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void* hks_start_stub(char a1)
|
void* hks_start_stub(char a1)
|
||||||
{
|
{
|
||||||
const auto _ = gsl::finally([]()
|
const auto _ = gsl::finally(&start);
|
||||||
{
|
|
||||||
ui_scripting::lua::engine::start();
|
|
||||||
});
|
|
||||||
|
|
||||||
return hks_start_hook.invoke<void*>(a1);
|
return hks_start_hook.invoke<void*>(a1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hks_shutdown_stub()
|
void hks_shutdown_stub()
|
||||||
{
|
{
|
||||||
ui_scripting::lua::engine::stop();
|
converted_functions.clear();
|
||||||
hks_shutdown_hook.invoke<void*>();
|
hks_shutdown_hook.invoke<void*>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void* hks_allocator_stub(void* userData, void* oldMemory, unsigned __int64 oldSize, unsigned __int64 newSize)
|
void hks_package_require_stub(game::hks::lua_State* state)
|
||||||
{
|
{
|
||||||
const auto closure = reinterpret_cast<game::hks::cclosure*>(oldMemory);
|
const auto script = get_current_script();
|
||||||
if (converted_functions.find(closure) != converted_functions.end())
|
const auto root = get_root_script(script);
|
||||||
|
globals.in_require_script = root;
|
||||||
|
hks_package_require_hook.invoke<void>(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::hook::detour db_find_xasset_header_hook;
|
||||||
|
game::XAssetHeader db_find_xasset_header_stub(game::XAssetType type, const char* name, int allow_create_default)
|
||||||
|
{
|
||||||
|
if (type != game::XAssetType::ASSET_TYPE_LUAFILE)
|
||||||
{
|
{
|
||||||
converted_functions.erase(closure);
|
return db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
|
if (!is_loaded_script(globals.in_require_script))
|
||||||
|
{
|
||||||
|
return db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto folder = globals.in_require_script.substr(0, globals.in_require_script.find_last_of("/\\"));
|
||||||
|
const std::string name_ = name;
|
||||||
|
const std::string target_script = folder + "/" + name_ + ".lua";
|
||||||
|
|
||||||
|
if (utils::io::file_exists(target_script))
|
||||||
|
{
|
||||||
|
globals.load_raw_script = true;
|
||||||
|
globals.raw_script_name = target_script;
|
||||||
|
return static_cast<game::XAssetHeader>(reinterpret_cast<game::LuaFile*>(1));
|
||||||
|
}
|
||||||
|
else if (name_.starts_with("ui/LUI/"))
|
||||||
|
{
|
||||||
|
return db_find_xasset_header_hook.invoke<game::XAssetHeader>(type, name, allow_create_default);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<game::XAssetHeader>(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void hks_frame_stub()
|
int hks_load_stub(game::hks::lua_State* state, void* compiler_options,
|
||||||
|
void* reader, void* reader_data, const char* chunk_name)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
if (globals.load_raw_script)
|
||||||
if (state)
|
|
||||||
{
|
{
|
||||||
ui_scripting::lua::engine::run_frame();
|
globals.load_raw_script = false;
|
||||||
|
globals.loaded_scripts.push_back({globals.raw_script_name, globals.in_require_script});
|
||||||
|
return load_buffer(globals.raw_script_name, utils::io::read_file(globals.raw_script_name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return utils::hook::invoke<int>(0x1402D9410, state, compiler_options, reader,
|
||||||
|
reader_data, chunk_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int main_function_handler(game::hks::lua_State* state)
|
int main_handler(game::hks::lua_State* state)
|
||||||
{
|
|
||||||
const auto value = state->m_apistack.base[-1];
|
|
||||||
if (value.t != game::hks::TCFUNCTION)
|
|
||||||
{
|
{
|
||||||
|
const auto value = state->m_apistack.base[-1];
|
||||||
|
if (value.t != game::hks::TCFUNCTION)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto closure = value.v.cClosure;
|
||||||
|
if (converted_functions.find(closure) == converted_functions.end())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& function = converted_functions[closure];
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const auto args = get_return_values();
|
||||||
|
const auto results = function(args);
|
||||||
|
|
||||||
|
for (const auto& result : results)
|
||||||
|
{
|
||||||
|
push_value(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(results.size());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
game::hks::hksi_luaL_error(state, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto closure = value.v.cClosure;
|
|
||||||
if (converted_functions.find(closure) == converted_functions.end())
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto function = converted_functions[closure];
|
|
||||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
|
||||||
const auto arguments = get_return_values(count);
|
|
||||||
const auto s = function.lua_state();
|
|
||||||
|
|
||||||
std::vector<sol::lua_value> converted_args;
|
|
||||||
|
|
||||||
for (const auto& argument : arguments)
|
|
||||||
{
|
|
||||||
converted_args.push_back(lua::convert(s, argument));
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto results = function(sol::as_args(converted_args));
|
|
||||||
lua::handle_error(results);
|
|
||||||
|
|
||||||
for (const auto& result : results)
|
|
||||||
{
|
|
||||||
push_value(lua::convert({s, result}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results.return_count();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function)
|
template <typename F>
|
||||||
|
game::hks::cclosure* convert_function(F f)
|
||||||
{
|
{
|
||||||
converted_functions[closure] = function;
|
const auto state = *game::hks::lua_state;
|
||||||
}
|
const auto closure = game::hks::cclosure_Create(state, main_handler, 0, 0, 0);
|
||||||
|
converted_functions[closure] = wrap_function(f);
|
||||||
void clear_converted_functions()
|
return closure;
|
||||||
{
|
|
||||||
converted_functions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable_error_hook()
|
|
||||||
{
|
|
||||||
error_hook_enabled++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void disable_error_hook()
|
|
||||||
{
|
|
||||||
error_hook_enabled--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class component final : public component_interface
|
class component final : public component_interface
|
||||||
@ -181,14 +470,15 @@ namespace ui_scripting
|
|||||||
|
|
||||||
void post_unpack() override
|
void post_unpack() override
|
||||||
{
|
{
|
||||||
hks_frame_hook.create(0x140327880, hks_frame_stub);
|
utils::hook::call(0x14030C079, db_find_xasset_header_stub);
|
||||||
|
utils::hook::call(0x14030C104, hks_load_stub);
|
||||||
|
utils::hook::jump(0x14013A98C, printf);
|
||||||
|
|
||||||
|
db_find_xasset_header_hook.create(game::DB_FindXAssetHeader, db_find_xasset_header_stub);
|
||||||
|
|
||||||
|
hks_package_require_hook.create(0x1402B4DA0, hks_package_require_stub);
|
||||||
hks_start_hook.create(0x140328BE0, hks_start_stub);
|
hks_start_hook.create(0x140328BE0, hks_start_stub);
|
||||||
hks_shutdown_hook.create(0x1403203B0, hks_shutdown_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);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,48 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "game/ui_scripting/lua/value_conversion.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
int main_function_handler(game::hks::lua_State* state);
|
template <class... Args, std::size_t... I>
|
||||||
void add_converted_function(game::hks::cclosure* closure, const sol::protected_function& function);
|
auto wrap_function(const std::function<void(Args...)>& f, std::index_sequence<I...>)
|
||||||
void clear_converted_functions();
|
{
|
||||||
|
return [f](const function_arguments& args)
|
||||||
|
{
|
||||||
|
f(args[I]...);
|
||||||
|
return arguments{{}};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
void enable_error_hook();
|
template <class... Args, std::size_t... I>
|
||||||
void disable_error_hook();
|
auto wrap_function(const std::function<arguments(Args...)>& f, std::index_sequence<I...>)
|
||||||
|
{
|
||||||
|
return [f](const function_arguments& args)
|
||||||
|
{
|
||||||
|
return f(args[I]...);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, class... Args, std::size_t... I>
|
||||||
|
auto wrap_function(const std::function<R(Args...)>& f, std::index_sequence<I...>)
|
||||||
|
{
|
||||||
|
return [f](const function_arguments& args)
|
||||||
|
{
|
||||||
|
return arguments{f(args[I]...)};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R, class... Args>
|
||||||
|
auto wrap_function(const std::function<R(Args...)>& f)
|
||||||
|
{
|
||||||
|
return wrap_function(f, std::index_sequence_for<Args...>{});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
auto wrap_function(F f)
|
||||||
|
{
|
||||||
|
std::function f_ = f;
|
||||||
|
return wrap_function(f_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
game::hks::cclosure* convert_function(F f);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
#include "updater.hpp"
|
#include "updater.hpp"
|
||||||
|
#include "game/ui_scripting/execution.hpp"
|
||||||
|
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
@ -70,6 +71,14 @@ namespace updater
|
|||||||
return main;
|
return main;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void notify(const std::string& name)
|
||||||
|
{
|
||||||
|
scheduler::once([=]()
|
||||||
|
{
|
||||||
|
ui_scripting::notify(name, {});
|
||||||
|
}, scheduler::pipeline::lui);
|
||||||
|
}
|
||||||
|
|
||||||
void set_update_check_status(bool done, bool success, const std::string& error = {})
|
void set_update_check_status(bool done, bool success, const std::string& error = {})
|
||||||
{
|
{
|
||||||
update_data.access([done, success, error](update_data_t& data_)
|
update_data.access([done, success, error](update_data_t& data_)
|
||||||
@ -77,6 +86,8 @@ namespace updater
|
|||||||
data_.check.done = done;
|
data_.check.done = done;
|
||||||
data_.check.success = success;
|
data_.check.success = success;
|
||||||
data_.error = error;
|
data_.error = error;
|
||||||
|
|
||||||
|
notify("update_check_done");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +98,7 @@ namespace updater
|
|||||||
data_.download.done = done;
|
data_.download.done = done;
|
||||||
data_.download.success = success;
|
data_.download.success = success;
|
||||||
data_.error = error;
|
data_.error = error;
|
||||||
|
notify("update_done");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,6 +342,8 @@ namespace updater
|
|||||||
data_.check.success = true;
|
data_.check.success = true;
|
||||||
data_.required_files = required_files;
|
data_.required_files = required_files;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
notify("update_check_done");
|
||||||
}, scheduler::pipeline::async);
|
}, scheduler::pipeline::async);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1081,6 +1081,10 @@ namespace game
|
|||||||
|
|
||||||
namespace hks
|
namespace hks
|
||||||
{
|
{
|
||||||
|
struct lua_State;
|
||||||
|
struct HashTable;
|
||||||
|
struct cclosure;
|
||||||
|
|
||||||
struct GenericChunkHeader
|
struct GenericChunkHeader
|
||||||
{
|
{
|
||||||
unsigned __int64 m_flags;
|
unsigned __int64 m_flags;
|
||||||
@ -1106,9 +1110,6 @@ namespace game
|
|||||||
char m_data[30];
|
char m_data[30];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HashTable;
|
|
||||||
struct cclosure;
|
|
||||||
|
|
||||||
union HksValue
|
union HksValue
|
||||||
{
|
{
|
||||||
cclosure* cClosure;
|
cclosure* cClosure;
|
||||||
@ -1120,6 +1121,8 @@ namespace game
|
|||||||
void* thread;
|
void* thread;
|
||||||
void* ptr;
|
void* ptr;
|
||||||
float number;
|
float number;
|
||||||
|
long long i64;
|
||||||
|
unsigned long long ui64;
|
||||||
unsigned int native;
|
unsigned int native;
|
||||||
bool boolean;
|
bool boolean;
|
||||||
};
|
};
|
||||||
@ -1233,24 +1236,6 @@ namespace game
|
|||||||
int is_tail_call;
|
int is_tail_call;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lua_State : ChunkHeader
|
|
||||||
{
|
|
||||||
void* m_global;
|
|
||||||
CallStack m_callStack;
|
|
||||||
ApiStack m_apistack;
|
|
||||||
UpValue* pending;
|
|
||||||
HksObject globals;
|
|
||||||
HksObject m_cEnv;
|
|
||||||
CallSite* m_callsites;
|
|
||||||
int m_numberOfCCalls;
|
|
||||||
void* m_context;
|
|
||||||
InternString* m_name;
|
|
||||||
lua_State* m_nextState;
|
|
||||||
lua_State* m_nextStateStack;
|
|
||||||
Status m_status;
|
|
||||||
HksError m_error;
|
|
||||||
};
|
|
||||||
|
|
||||||
using lua_function = int(__fastcall*)(lua_State*);
|
using lua_function = int(__fastcall*)(lua_State*);
|
||||||
|
|
||||||
struct luaL_Reg
|
struct luaL_Reg
|
||||||
@ -1289,5 +1274,231 @@ namespace game
|
|||||||
InternString* m_name;
|
InternString* m_name;
|
||||||
HksObject m_upvalues[1];
|
HksObject m_upvalues[1];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum HksCompilerSettings_BytecodeSharingFormat
|
||||||
|
{
|
||||||
|
BYTECODE_DEFAULT = 0x0,
|
||||||
|
BYTECODE_INPLACE = 0x1,
|
||||||
|
BYTECODE_REFERENCED = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HksCompilerSettings_IntLiteralOptions
|
||||||
|
{
|
||||||
|
INT_LITERALS_NONE = 0x0,
|
||||||
|
INT_LITERALS_LUD = 0x1,
|
||||||
|
INT_LITERALS_32BIT = 0x1,
|
||||||
|
INT_LITERALS_UI64 = 0x2,
|
||||||
|
INT_LITERALS_64BIT = 0x2,
|
||||||
|
INT_LITERALS_ALL = 0x3,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HksCompilerSettings
|
||||||
|
{
|
||||||
|
int m_emitStructCode;
|
||||||
|
const char** m_stripNames;
|
||||||
|
int m_emitGlobalMemoization;
|
||||||
|
int _m_isHksGlobalMemoTestingMode;
|
||||||
|
HksCompilerSettings_BytecodeSharingFormat m_bytecodeSharingFormat;
|
||||||
|
HksCompilerSettings_IntLiteralOptions m_enableIntLiterals;
|
||||||
|
int(__fastcall* m_debugMap)(const char*, int);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HksBytecodeSharingMode
|
||||||
|
{
|
||||||
|
HKS_BYTECODE_SHARING_OFF = 0x0,
|
||||||
|
HKS_BYTECODE_SHARING_ON = 0x1,
|
||||||
|
HKS_BYTECODE_SHARING_SECURE = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HksGcWeights
|
||||||
|
{
|
||||||
|
int m_removeString;
|
||||||
|
int m_finalizeUserdataNoMM;
|
||||||
|
int m_finalizeUserdataGcMM;
|
||||||
|
int m_cleanCoroutine;
|
||||||
|
int m_removeWeak;
|
||||||
|
int m_markObject;
|
||||||
|
int m_traverseString;
|
||||||
|
int m_traverseUserdata;
|
||||||
|
int m_traverseCoroutine;
|
||||||
|
int m_traverseWeakTable;
|
||||||
|
int m_freeChunk;
|
||||||
|
int m_sweepTraverse;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GarbageCollector_Stack
|
||||||
|
{
|
||||||
|
void* m_storage;
|
||||||
|
unsigned int m_numEntries;
|
||||||
|
unsigned int m_numAllocated;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProtoList
|
||||||
|
{
|
||||||
|
void** m_protoList;
|
||||||
|
unsigned __int16 m_protoSize;
|
||||||
|
unsigned __int16 m_protoAllocSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GarbageCollector
|
||||||
|
{
|
||||||
|
int m_target;
|
||||||
|
int m_stepsLeft;
|
||||||
|
int m_stepLimit;
|
||||||
|
HksGcWeights m_costs;
|
||||||
|
int m_unit;
|
||||||
|
_SETJMP_FLOAT128(*m_jumpPoint)[16];
|
||||||
|
lua_State* m_mainState;
|
||||||
|
lua_State* m_finalizerState;
|
||||||
|
void* m_memory;
|
||||||
|
int m_phase;
|
||||||
|
GarbageCollector_Stack m_resumeStack;
|
||||||
|
GarbageCollector_Stack m_greyStack;
|
||||||
|
GarbageCollector_Stack m_remarkStack;
|
||||||
|
GarbageCollector_Stack m_weakStack;
|
||||||
|
int m_finalizing;
|
||||||
|
HksObject m_safeTableValue;
|
||||||
|
lua_State* m_startOfStateStackList;
|
||||||
|
lua_State* m_endOfStateStackList;
|
||||||
|
lua_State* m_currentState;
|
||||||
|
HksObject m_safeValue;
|
||||||
|
void* m_compiler;
|
||||||
|
void* m_bytecodeReader;
|
||||||
|
void* m_bytecodeWriter;
|
||||||
|
int m_pauseMultiplier;
|
||||||
|
int m_stepMultiplier;
|
||||||
|
bool m_stopped;
|
||||||
|
int(__fastcall* m_gcPolicy)(lua_State*);
|
||||||
|
unsigned __int64 m_pauseTriggerMemoryUsage;
|
||||||
|
int m_stepTriggerCountdown;
|
||||||
|
unsigned int m_stringTableIndex;
|
||||||
|
unsigned int m_stringTableSize;
|
||||||
|
UserData* m_lastBlackUD;
|
||||||
|
UserData* m_activeUD;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MemoryManager_ChunkColor
|
||||||
|
{
|
||||||
|
RED = 0x0,
|
||||||
|
BLACK = 0x1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkList
|
||||||
|
{
|
||||||
|
ChunkHeader m_prevToStart;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Hks_DeleteCheckingMode
|
||||||
|
{
|
||||||
|
HKS_DELETE_CHECKING_OFF = 0x0,
|
||||||
|
HKS_DELETE_CHECKING_ACCURATE = 0x1,
|
||||||
|
HKS_DELETE_CHECKING_SAFE = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MemoryManager
|
||||||
|
{
|
||||||
|
void* (__fastcall* m_allocator)(void*, void*, unsigned __int64, unsigned __int64);
|
||||||
|
void* m_allocatorUd;
|
||||||
|
MemoryManager_ChunkColor m_chunkColor;
|
||||||
|
unsigned __int64 m_used;
|
||||||
|
unsigned __int64 m_highwatermark;
|
||||||
|
ChunkList m_allocationList;
|
||||||
|
ChunkList m_sweepList;
|
||||||
|
ChunkHeader* m_lastKeptChunk;
|
||||||
|
lua_State* m_state;
|
||||||
|
ChunkList m_deletedList;
|
||||||
|
int m_deleteMode;
|
||||||
|
Hks_DeleteCheckingMode m_deleteCheckingMode;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StaticStringCache
|
||||||
|
{
|
||||||
|
HksObject m_objects[41];
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HksBytecodeEndianness
|
||||||
|
{
|
||||||
|
HKS_BYTECODE_DEFAULT_ENDIAN = 0x0,
|
||||||
|
HKS_BYTECODE_BIG_ENDIAN = 0x1,
|
||||||
|
HKS_BYTECODE_LITTLE_ENDIAN = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeProfileData_Stats
|
||||||
|
{
|
||||||
|
unsigned __int64 hksTime;
|
||||||
|
unsigned __int64 callbackTime;
|
||||||
|
unsigned __int64 gcTime;
|
||||||
|
unsigned __int64 cFinalizerTime;
|
||||||
|
unsigned __int64 compilerTime;
|
||||||
|
unsigned int hkssTimeSamples;
|
||||||
|
unsigned int callbackTimeSamples;
|
||||||
|
unsigned int gcTimeSamples;
|
||||||
|
unsigned int compilerTimeSamples;
|
||||||
|
unsigned int num_newuserdata;
|
||||||
|
unsigned int num_tablerehash;
|
||||||
|
unsigned int num_pushstring;
|
||||||
|
unsigned int num_pushcfunction;
|
||||||
|
unsigned int num_newtables;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RuntimeProfileData
|
||||||
|
{
|
||||||
|
__int64 stackDepth;
|
||||||
|
__int64 callbackDepth;
|
||||||
|
unsigned __int64 lastTimer;
|
||||||
|
RuntimeProfileData_Stats frameStats;
|
||||||
|
unsigned __int64 gcStartTime;
|
||||||
|
unsigned __int64 finalizerStartTime;
|
||||||
|
unsigned __int64 compilerStartTime;
|
||||||
|
unsigned __int64 compilerStartGCTime;
|
||||||
|
unsigned __int64 compilerStartGCFinalizerTime;
|
||||||
|
unsigned __int64 compilerCallbackStartTime;
|
||||||
|
__int64 compilerDepth;
|
||||||
|
void* outFile;
|
||||||
|
lua_State* rootState;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct HksGlobal
|
||||||
|
{
|
||||||
|
MemoryManager m_memory;
|
||||||
|
GarbageCollector m_collector;
|
||||||
|
StringTable m_stringTable;
|
||||||
|
HksBytecodeSharingMode m_bytecodeSharingMode;
|
||||||
|
unsigned int m_tableVersionInitializer;
|
||||||
|
HksObject m_registry;
|
||||||
|
ProtoList m_protoList;
|
||||||
|
HashTable* m_structProtoByName;
|
||||||
|
ChunkList m_userDataList;
|
||||||
|
lua_State* m_root;
|
||||||
|
StaticStringCache m_staticStringCache;
|
||||||
|
void* m_debugger;
|
||||||
|
void* m_profiler;
|
||||||
|
RuntimeProfileData m_runProfilerData;
|
||||||
|
HksCompilerSettings m_compilerSettings;
|
||||||
|
int(__fastcall* m_panicFunction)(lua_State*);
|
||||||
|
void* m_luaplusObjectList;
|
||||||
|
int m_heapAssertionFrequency;
|
||||||
|
int m_heapAssertionCount;
|
||||||
|
void (*m_logFunction)(lua_State*, const char*, ...);
|
||||||
|
HksBytecodeEndianness m_bytecodeDumpEndianness;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct lua_State : ChunkHeader
|
||||||
|
{
|
||||||
|
HksGlobal* m_global;
|
||||||
|
CallStack m_callStack;
|
||||||
|
ApiStack m_apistack;
|
||||||
|
UpValue* pending;
|
||||||
|
HksObject globals;
|
||||||
|
HksObject m_cEnv;
|
||||||
|
CallSite* m_callsites;
|
||||||
|
int m_numberOfCCalls;
|
||||||
|
void* m_context;
|
||||||
|
InternString* m_name;
|
||||||
|
lua_State* m_nextState;
|
||||||
|
lua_State* m_nextStateStack;
|
||||||
|
Status m_status;
|
||||||
|
HksError m_error;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,5 +211,11 @@ namespace game
|
|||||||
WEAK symbol<void(lua_State* s, int index, const char* k)> hksi_lua_setfield{0x1402DEA30};
|
WEAK symbol<void(lua_State* s, int index, const char* k)> hksi_lua_setfield{0x1402DEA30};
|
||||||
WEAK symbol<int(lua_State* s, int nargs, int nresults, int errfunc)> hksi_lua_pcall{0x1402DDE50};
|
WEAK symbol<int(lua_State* s, int nargs, int nresults, int errfunc)> hksi_lua_pcall{0x1402DDE50};
|
||||||
WEAK symbol<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x1402CBAD0};
|
WEAK symbol<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x1402CBAD0};
|
||||||
|
WEAK symbol<int(lua_State* s, const HksCompilerSettings* options, const char* buff,
|
||||||
|
unsigned __int64 sz, const char* name)> hksi_hksL_loadbuffer{0x1402DB8B0};
|
||||||
|
WEAK symbol<int(lua_State* s, const char* what, lua_Debug* ar)> hksi_lua_getinfo{0x1402DD1F0};
|
||||||
|
WEAK symbol<int(lua_State* s, int level, lua_Debug* ar)> hksi_lua_getstack{0x1402DD4C0};
|
||||||
|
WEAK symbol<void(lua_State* s, const char* fmt, ...)> hksi_luaL_error{0x1402E3E40};
|
||||||
|
WEAK symbol<const char*> typenames{0x140BE9F50};
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,19 +14,29 @@ namespace ui_scripting
|
|||||||
state->m_apistack.top++;
|
state->m_apistack.top++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void push_value(const game::hks::HksObject& value)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
*state->m_apistack.top = value;
|
||||||
|
state->m_apistack.top++;
|
||||||
|
}
|
||||||
|
|
||||||
script_value get_return_value(int offset)
|
script_value get_return_value(int offset)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
return state->m_apistack.top[-1 - offset];
|
return state->m_apistack.top[-1 - offset];
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments get_return_values(int count)
|
arguments get_return_values()
|
||||||
{
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
||||||
arguments values;
|
arguments values;
|
||||||
|
|
||||||
for (auto i = count - 1; i >= 0; i--)
|
for (auto i = count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
values.push_back(get_return_value(i));
|
const auto v = get_return_value(i);
|
||||||
|
values.push_back(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (values.size() == 0)
|
if (values.size() == 0)
|
||||||
@ -48,37 +58,30 @@ namespace ui_scripting
|
|||||||
const auto _1 = gsl::finally(game::LUI_LeaveCriticalSection);
|
const auto _1 = gsl::finally(game::LUI_LeaveCriticalSection);
|
||||||
game::LUI_EnterCriticalSection();
|
game::LUI_EnterCriticalSection();
|
||||||
|
|
||||||
try
|
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
|
||||||
|
const auto engine = globals.get("Engine").as<table>();
|
||||||
|
const auto root = engine.get("GetLuiRoot").as<function>().call({})[0].as<userdata>();
|
||||||
|
const auto process_event = root.get("processEvent").as<function>();
|
||||||
|
|
||||||
|
table event{};
|
||||||
|
event.set("name", name);
|
||||||
|
event.set("dispatchChildren", true);
|
||||||
|
|
||||||
|
for (const auto& arg : arguments)
|
||||||
{
|
{
|
||||||
const auto globals = table((*::game::hks::lua_state)->globals.v.table);
|
event.set(arg.first, arg.second);
|
||||||
const auto engine = globals.get("Engine").as<table>();
|
|
||||||
const auto root = engine.get("GetLuiRoot").as<function>().call({})[0].as<userdata>();
|
|
||||||
const auto process_event = root.get("processEvent").as<function>();
|
|
||||||
|
|
||||||
table event{};
|
|
||||||
event.set("name", name);
|
|
||||||
event.set("dispatchChildren", true);
|
|
||||||
|
|
||||||
for (const auto& arg : arguments)
|
|
||||||
{
|
|
||||||
event.set(arg.first, arg.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
process_event.call({root, event});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
printf("Error processing event '%s' %s\n", name.data(), e.what());
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_event.call({root, event});
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
arguments call_script_function(const function& function, const arguments& arguments)
|
arguments call_script_function(const function& function, const arguments& arguments)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
|
state->m_apistack.top = state->m_apistack.base;
|
||||||
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
stack stack;
|
|
||||||
push_value(function);
|
push_value(function);
|
||||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||||
{
|
{
|
||||||
@ -86,127 +89,66 @@ namespace ui_scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto num_args = static_cast<int>(arguments.size());
|
const auto num_args = static_cast<int>(arguments.size());
|
||||||
stack.save(num_args + 1);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
game::hks::vm_call_internal(state, num_args, -1, 0);
|
||||||
enable_error_hook();
|
const auto args = get_return_values();
|
||||||
|
state->m_apistack.top = top;
|
||||||
try
|
return args;
|
||||||
{
|
|
||||||
game::hks::vm_call_internal(state, num_args, -1, 0);
|
|
||||||
const auto count = static_cast<int>(state->m_apistack.top - state->m_apistack.base);
|
|
||||||
return get_return_values(count);
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
stack.fix();
|
|
||||||
throw std::runtime_error("Error executing script function: "s + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value get_field(const userdata& self, const script_value& key)
|
script_value get_field(const userdata& self, const script_value& key)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
stack stack;
|
|
||||||
push_value(key);
|
push_value(key);
|
||||||
stack.save(1);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
game::hks::HksObject value{};
|
||||||
game::hks::HksObject userdata{};
|
game::hks::HksObject userdata{};
|
||||||
userdata.t = game::hks::TUSERDATA;
|
userdata.t = game::hks::TUSERDATA;
|
||||||
userdata.v.ptr = self.ptr;
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
try
|
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||||
{
|
state->m_apistack.top = top;
|
||||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
return value;
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
stack.fix();
|
|
||||||
throw std::runtime_error("Error getting userdata field: "s + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value get_field(const table& self, const script_value& key)
|
script_value get_field(const table& self, const script_value& key)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
stack stack;
|
|
||||||
push_value(key);
|
push_value(key);
|
||||||
stack.save(1);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
game::hks::HksObject value{};
|
||||||
game::hks::HksObject userdata{};
|
game::hks::HksObject userdata{};
|
||||||
userdata.t = game::hks::TTABLE;
|
userdata.t = game::hks::TTABLE;
|
||||||
userdata.v.ptr = self.ptr;
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
try
|
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
||||||
{
|
state->m_apistack.top = top;
|
||||||
game::hks::hks_obj_gettable(&value, state, &userdata, &state->m_apistack.top[-1]);
|
return value;
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
stack.fix();
|
|
||||||
throw std::runtime_error("Error getting table field: "s + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
void set_field(const userdata& self, const script_value& key, const script_value& value)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
stack stack;
|
|
||||||
stack.save(0);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject userdata{};
|
game::hks::HksObject userdata{};
|
||||||
userdata.t = game::hks::TUSERDATA;
|
userdata.t = game::hks::TUSERDATA;
|
||||||
userdata.v.ptr = self.ptr;
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
try
|
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||||
{
|
|
||||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
stack.fix();
|
|
||||||
throw std::runtime_error("Error setting userdata field: "s + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_field(const table& self, const script_value& key, const script_value& value)
|
void set_field(const table& self, const script_value& key, const script_value& value)
|
||||||
{
|
{
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
|
|
||||||
stack stack;
|
|
||||||
stack.save(0);
|
|
||||||
|
|
||||||
const auto _1 = gsl::finally(&disable_error_hook);
|
|
||||||
enable_error_hook();
|
|
||||||
|
|
||||||
game::hks::HksObject userdata{};
|
game::hks::HksObject userdata{};
|
||||||
userdata.t = game::hks::TTABLE;
|
userdata.t = game::hks::TTABLE;
|
||||||
userdata.v.ptr = self.ptr;
|
userdata.v.ptr = self.ptr;
|
||||||
|
|
||||||
try
|
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
||||||
{
|
|
||||||
game::hks::hks_obj_settable(state, &userdata, &key.get_raw(), &value.get_raw());
|
|
||||||
}
|
|
||||||
catch (const std::exception& e)
|
|
||||||
{
|
|
||||||
stack.fix();
|
|
||||||
throw std::runtime_error("Error setting table field: "s + e.what());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
void push_value(const script_value& value);
|
void push_value(const script_value& value);
|
||||||
|
void push_value(const game::hks::HksObject& value);
|
||||||
|
|
||||||
script_value get_return_value(int offset);
|
script_value get_return_value(int offset);
|
||||||
arguments get_return_values(int count);
|
arguments get_return_values();
|
||||||
|
|
||||||
bool notify(const std::string& name, const event_arguments& arguments);
|
bool notify(const std::string& name, const event_arguments& arguments);
|
||||||
|
|
||||||
|
@ -1,618 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
#include "../../scripting/execution.hpp"
|
|
||||||
#include "../script_value.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
#include "../../../component/scripting.hpp"
|
|
||||||
#include "../../../component/command.hpp"
|
|
||||||
#include "../../../component/fastfiles.hpp"
|
|
||||||
#include "../../../component/updater.hpp"
|
|
||||||
#include "../../../component/localized_strings.hpp"
|
|
||||||
#include "../../../component/mods.hpp"
|
|
||||||
|
|
||||||
#include "component/game_console.hpp"
|
|
||||||
#include "component/scheduler.hpp"
|
|
||||||
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <utils/http.hpp>
|
|
||||||
#include <utils/cryptography.hpp>
|
|
||||||
#include <version.h>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const auto json_script = utils::nt::load_resource(LUA_JSON);
|
|
||||||
|
|
||||||
bool valid_dvar_name(const std::string& name)
|
|
||||||
{
|
|
||||||
for (const auto c : name)
|
|
||||||
{
|
|
||||||
if (!isalnum(c) && c != '_')
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_io(sol::state& state)
|
|
||||||
{
|
|
||||||
state["io"]["fileexists"] = utils::io::file_exists;
|
|
||||||
state["io"]["writefile"] = utils::io::write_file;
|
|
||||||
state["io"]["movefile"] = utils::io::move_file;
|
|
||||||
state["io"]["filesize"] = utils::io::file_size;
|
|
||||||
state["io"]["createdirectory"] = utils::io::create_directory;
|
|
||||||
state["io"]["directoryexists"] = utils::io::directory_exists;
|
|
||||||
state["io"]["directoryisempty"] = utils::io::directory_is_empty;
|
|
||||||
state["io"]["listfiles"] = utils::io::list_files;
|
|
||||||
state["io"]["copyfolder"] = utils::io::copy_folder;
|
|
||||||
state["io"]["removefile"] = utils::io::remove_file;
|
|
||||||
state["io"]["removedirectory"] = utils::io::remove_directory;
|
|
||||||
state["io"]["readfile"] = static_cast<std::string(*)(const std::string&)>(utils::io::read_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_json(sol::state& state)
|
|
||||||
{
|
|
||||||
const auto json = state.safe_script(json_script, &sol::script_pass_on_error);
|
|
||||||
handle_error(json);
|
|
||||||
state["json"] = json;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_vector_type(sol::state& state)
|
|
||||||
{
|
|
||||||
auto vector_type = state.new_usertype<scripting::vector>("vector", sol::constructors<scripting::vector(float, float, float)>());
|
|
||||||
vector_type["x"] = sol::property(&scripting::vector::get_x, &scripting::vector::set_x);
|
|
||||||
vector_type["y"] = sol::property(&scripting::vector::get_y, &scripting::vector::set_y);
|
|
||||||
vector_type["z"] = sol::property(&scripting::vector::get_z, &scripting::vector::set_z);
|
|
||||||
|
|
||||||
vector_type["r"] = sol::property(&scripting::vector::get_x, &scripting::vector::set_x);
|
|
||||||
vector_type["g"] = sol::property(&scripting::vector::get_y, &scripting::vector::set_y);
|
|
||||||
vector_type["b"] = sol::property(&scripting::vector::get_z, &scripting::vector::set_z);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::addition] = sol::overload(
|
|
||||||
[](const scripting::vector& a, const scripting::vector& b)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() + b.get_x(),
|
|
||||||
a.get_y() + b.get_y(),
|
|
||||||
a.get_z() + b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const scripting::vector& a, const int value)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() + value,
|
|
||||||
a.get_y() + value,
|
|
||||||
a.get_z() + value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::subtraction] = sol::overload(
|
|
||||||
[](const scripting::vector& a, const scripting::vector& b)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() - b.get_x(),
|
|
||||||
a.get_y() - b.get_y(),
|
|
||||||
a.get_z() - b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const scripting::vector& a, const int value)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() - value,
|
|
||||||
a.get_y() - value,
|
|
||||||
a.get_z() - value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::multiplication] = sol::overload(
|
|
||||||
[](const scripting::vector& a, const scripting::vector& b)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() * b.get_x(),
|
|
||||||
a.get_y() * b.get_y(),
|
|
||||||
a.get_z() * b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const scripting::vector& a, const int value)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() * value,
|
|
||||||
a.get_y() * value,
|
|
||||||
a.get_z() * value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::division] = sol::overload(
|
|
||||||
[](const scripting::vector& a, const scripting::vector& b)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() / b.get_x(),
|
|
||||||
a.get_y() / b.get_y(),
|
|
||||||
a.get_z() / b.get_z()
|
|
||||||
);
|
|
||||||
},
|
|
||||||
[](const scripting::vector& a, const int value)
|
|
||||||
{
|
|
||||||
return scripting::vector(
|
|
||||||
a.get_x() / value,
|
|
||||||
a.get_y() / value,
|
|
||||||
a.get_z() / value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::equal_to] = [](const scripting::vector& a, const scripting::vector& b)
|
|
||||||
{
|
|
||||||
return a.get_x() == b.get_x() &&
|
|
||||||
a.get_y() == b.get_y() &&
|
|
||||||
a.get_z() == b.get_z();
|
|
||||||
};
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::length] = [](const scripting::vector& a)
|
|
||||||
{
|
|
||||||
return sqrt((a.get_x() * a.get_x()) + (a.get_y() * a.get_y()) + (a.get_z() * a.get_z()));
|
|
||||||
};
|
|
||||||
|
|
||||||
vector_type[sol::meta_function::to_string] = [](const scripting::vector& a)
|
|
||||||
{
|
|
||||||
return utils::string::va("{x: %f, y: %f, z: %f}", a.get_x(), a.get_y(), a.get_z());
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_game_type(sol::state& state, scheduler& scheduler)
|
|
||||||
{
|
|
||||||
struct game
|
|
||||||
{
|
|
||||||
};
|
|
||||||
auto game_type = state.new_usertype<game>("game_");
|
|
||||||
state["game"] = game();
|
|
||||||
|
|
||||||
game_type["time"] = []()
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
|
|
||||||
return now.count();
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["executecommand"] = [](const game&, const std::string& command)
|
|
||||||
{
|
|
||||||
command::execute(command, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["onframe"] = [&scheduler](const game&, const sol::protected_function& callback)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, 0, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["ontimeout"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["oninterval"] = [&scheduler](const game&, const sol::protected_function& callback,
|
|
||||||
const long long milliseconds)
|
|
||||||
{
|
|
||||||
return scheduler.add(callback, milliseconds, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["isingame"] = []()
|
|
||||||
{
|
|
||||||
return ::game::CL_IsCgameInitialized() && ::game::g_entities[0].client;
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getdvar"] = [](const game&, const sol::this_state s, const std::string& name)
|
|
||||||
{
|
|
||||||
const auto dvar = ::game::Dvar_FindVar(name.data());
|
|
||||||
if (!dvar)
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string value = ::game::Dvar_ValueToString(dvar, nullptr, &dvar->current);
|
|
||||||
return sol::lua_value{s, value};
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getdvarint"] = [](const game&, const sol::this_state s, const std::string& name)
|
|
||||||
{
|
|
||||||
const auto dvar = ::game::Dvar_FindVar(name.data());
|
|
||||||
if (!dvar)
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto value = atoi(::game::Dvar_ValueToString(dvar, nullptr, &dvar->current));
|
|
||||||
return sol::lua_value{s, value};
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getdvarfloat"] = [](const game&, const sol::this_state s, const std::string& name)
|
|
||||||
{
|
|
||||||
const auto dvar = ::game::Dvar_FindVar(name.data());
|
|
||||||
if (!dvar)
|
|
||||||
{
|
|
||||||
return sol::lua_value{s, sol::lua_nil};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto value = atof(::game::Dvar_ValueToString(dvar, nullptr, &dvar->current));
|
|
||||||
return sol::lua_value{s, value};
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["setdvar"] = [](const game&, const std::string& name, const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
if (!valid_dvar_name(name))
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid DVAR name, must be alphanumeric");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto hash = ::game::generateHashValue(name.data());
|
|
||||||
std::string string_value;
|
|
||||||
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
string_value = utils::string::va("%i", value.as<bool>());
|
|
||||||
}
|
|
||||||
else if (value.is<int>())
|
|
||||||
{
|
|
||||||
string_value = utils::string::va("%i", value.as<int>());
|
|
||||||
}
|
|
||||||
else if (value.is<float>())
|
|
||||||
{
|
|
||||||
string_value = utils::string::va("%f", value.as<float>());
|
|
||||||
}
|
|
||||||
else if (value.is<scripting::vector>())
|
|
||||||
{
|
|
||||||
const auto v = value.as<scripting::vector>();
|
|
||||||
string_value = utils::string::va("%f %f %f",
|
|
||||||
v.get_x(),
|
|
||||||
v.get_y(),
|
|
||||||
v.get_z()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
string_value = value.as<std::string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
::game::Dvar_SetCommand(hash, "", string_value.data());
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getwindowsize"] = [](const game&, const sol::this_state s)
|
|
||||||
{
|
|
||||||
const auto size = ::game::ScrPlace_GetViewPlacement()->realViewportSize;
|
|
||||||
|
|
||||||
auto screen = sol::table::create(s.lua_state());
|
|
||||||
screen["x"] = size[0];
|
|
||||||
screen["y"] = size[1];
|
|
||||||
|
|
||||||
return screen;
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["playmenuvideo"] = [](const game&, const std::string& video)
|
|
||||||
{
|
|
||||||
reinterpret_cast<void (*)(const char* a1, int a2, int a3)>
|
|
||||||
(0x14071B970)(video.data(), 64, 0);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["sharedset"] = [](const game&, const std::string& key, const std::string& value)
|
|
||||||
{
|
|
||||||
scripting::shared_table.access([key, value](scripting::shared_table_t& table)
|
|
||||||
{
|
|
||||||
table[key] = value;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["sharedget"] = [](const game&, const std::string& key)
|
|
||||||
{
|
|
||||||
std::string result;
|
|
||||||
scripting::shared_table.access([key, &result](scripting::shared_table_t& table)
|
|
||||||
{
|
|
||||||
result = table[key];
|
|
||||||
});
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["sharedclear"] = [](const game&)
|
|
||||||
{
|
|
||||||
scripting::shared_table.access([](scripting::shared_table_t& table)
|
|
||||||
{
|
|
||||||
table.clear();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["assetlist"] = [](const game&, const sol::this_state s, const std::string& type_string)
|
|
||||||
{
|
|
||||||
auto table = sol::table::create(s.lua_state());
|
|
||||||
auto index = 1;
|
|
||||||
auto type_index = -1;
|
|
||||||
|
|
||||||
for (auto i = 0; i < ::game::XAssetType::ASSET_TYPE_COUNT; i++)
|
|
||||||
{
|
|
||||||
if (type_string == ::game::g_assetNames[i])
|
|
||||||
{
|
|
||||||
type_index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type_index == -1)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Asset type does not exist");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto type = static_cast<::game::XAssetType>(type_index);
|
|
||||||
fastfiles::enum_assets(type, [type, &table, &index](const ::game::XAssetHeader header)
|
|
||||||
{
|
|
||||||
const auto asset = ::game::XAsset{type, header};
|
|
||||||
const std::string asset_name = ::game::DB_GetXAssetName(&asset);
|
|
||||||
table[index++] = asset_name;
|
|
||||||
}, true);
|
|
||||||
|
|
||||||
return table;
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getweapondisplayname"] = [](const game&, const std::string& name)
|
|
||||||
{
|
|
||||||
const auto alternate = name.starts_with("alt_");
|
|
||||||
const auto weapon = ::game::G_GetWeaponForName(name.data());
|
|
||||||
|
|
||||||
char buffer[0x400] = {0};
|
|
||||||
::game::CG_GetWeaponDisplayName(weapon, alternate, buffer, 0x400);
|
|
||||||
|
|
||||||
return std::string(buffer);
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["getloadedmod"] = [](const game&)
|
|
||||||
{
|
|
||||||
return mods::mod_path;
|
|
||||||
};
|
|
||||||
|
|
||||||
game_type["addlocalizedstring"] = [](const game&, const std::string& string,
|
|
||||||
const std::string& value)
|
|
||||||
{
|
|
||||||
localized_strings::override(string, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct player
|
|
||||||
{
|
|
||||||
};
|
|
||||||
auto player_type = state.new_usertype<player>("player_");
|
|
||||||
state["player"] = player();
|
|
||||||
|
|
||||||
player_type["notify"] = [](const player&, const sol::this_state s, const std::string& name, sol::variadic_args va)
|
|
||||||
{
|
|
||||||
if (!::game::CL_IsCgameInitialized() || !::game::g_entities[0].client)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Not in game");
|
|
||||||
}
|
|
||||||
|
|
||||||
const sol::state_view view{s};
|
|
||||||
const auto to_string = view["tostring"].get<sol::protected_function>();
|
|
||||||
|
|
||||||
std::vector<std::string> args{};
|
|
||||||
for (auto arg : va)
|
|
||||||
{
|
|
||||||
args.push_back(to_string.call(arg).get<std::string>());
|
|
||||||
}
|
|
||||||
|
|
||||||
::scheduler::once([s, name, args]()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::vector<scripting::script_value> arguments{};
|
|
||||||
|
|
||||||
for (const auto& arg : args)
|
|
||||||
{
|
|
||||||
arguments.push_back(arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto player = scripting::call("getentbynum", {0}).as<scripting::entity>();
|
|
||||||
scripting::notify(player, name, arguments);
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}, ::scheduler::pipeline::server);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup_lui_types(sol::state& state)
|
|
||||||
{
|
|
||||||
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[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"];
|
|
||||||
|
|
||||||
auto updater_table = sol::table::create(state.lua_state());
|
|
||||||
|
|
||||||
updater_table["relaunch"] = updater::relaunch;
|
|
||||||
|
|
||||||
updater_table["sethastriedupdate"] = updater::set_has_tried_update;
|
|
||||||
updater_table["gethastriedupdate"] = updater::get_has_tried_update;
|
|
||||||
updater_table["autoupdatesenabled"] = updater::auto_updates_enabled;
|
|
||||||
|
|
||||||
updater_table["startupdatecheck"] = updater::start_update_check;
|
|
||||||
updater_table["isupdatecheckdone"] = updater::is_update_check_done;
|
|
||||||
updater_table["getupdatecheckstatus"] = updater::get_update_check_status;
|
|
||||||
updater_table["isupdateavailable"] = updater::is_update_available;
|
|
||||||
|
|
||||||
updater_table["startupdatedownload"] = updater::start_update_download;
|
|
||||||
updater_table["isupdatedownloaddone"] = updater::is_update_download_done;
|
|
||||||
updater_table["getupdatedownloadstatus"] = updater::get_update_download_status;
|
|
||||||
updater_table["cancelupdate"] = updater::cancel_update;
|
|
||||||
updater_table["isrestartrequired"] = updater::is_restart_required;
|
|
||||||
|
|
||||||
updater_table["getlasterror"] = updater::get_last_error;
|
|
||||||
updater_table["getcurrentfile"] = updater::get_current_file;
|
|
||||||
|
|
||||||
state["updater"] = updater_table;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context::context(std::string data, script_type type)
|
|
||||||
: scheduler_(state_)
|
|
||||||
{
|
|
||||||
this->state_.open_libraries(sol::lib::base,
|
|
||||||
sol::lib::package,
|
|
||||||
sol::lib::io,
|
|
||||||
sol::lib::string,
|
|
||||||
sol::lib::os,
|
|
||||||
sol::lib::math,
|
|
||||||
sol::lib::table);
|
|
||||||
|
|
||||||
setup_io(this->state_);
|
|
||||||
setup_json(this->state_);
|
|
||||||
setup_vector_type(this->state_);
|
|
||||||
setup_game_type(this->state_, this->scheduler_);
|
|
||||||
setup_lui_types(this->state_);
|
|
||||||
|
|
||||||
if (type == script_type::file)
|
|
||||||
{
|
|
||||||
this->folder_ = data;
|
|
||||||
|
|
||||||
this->state_["include"] = [this](const std::string& file)
|
|
||||||
{
|
|
||||||
this->load_script(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
sol::function old_require = this->state_["require"];
|
|
||||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
|
||||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
|
||||||
{
|
|
||||||
return old_require(base_path + path);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->state_["scriptdir"] = [this]()
|
|
||||||
{
|
|
||||||
return this->folder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
printf("Loading ui script '%s'\n", this->folder_.data());
|
|
||||||
this->load_script("__init__");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == script_type::code)
|
|
||||||
{
|
|
||||||
handle_error(this->state_.safe_script(data, &sol::script_pass_on_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context::~context()
|
|
||||||
{
|
|
||||||
this->state_.collect_garbage();
|
|
||||||
this->scheduler_.clear();
|
|
||||||
this->state_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::run_frame()
|
|
||||||
{
|
|
||||||
this->scheduler_.run_frame();
|
|
||||||
this->state_.collect_garbage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void context::load_script(const std::string& script)
|
|
||||||
{
|
|
||||||
if (!this->loaded_scripts_.emplace(script).second)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto file = (std::filesystem::path{this->folder_} / (script + ".lua")).generic_string();
|
|
||||||
handle_error(this->state_.safe_script_file(file, &sol::script_pass_on_error));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable: 4702)
|
|
||||||
|
|
||||||
#define SOL_ALL_SAFETIES_ON 1
|
|
||||||
#define SOL_PRINT_ERRORS 0
|
|
||||||
#include <sol/sol.hpp>
|
|
||||||
|
|
||||||
#include "scheduler.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
enum script_type
|
|
||||||
{
|
|
||||||
file,
|
|
||||||
code
|
|
||||||
};
|
|
||||||
|
|
||||||
class context
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
context(std::string data, script_type type);
|
|
||||||
~context();
|
|
||||||
|
|
||||||
context(context&&) noexcept = delete;
|
|
||||||
context& operator=(context&&) noexcept = delete;
|
|
||||||
|
|
||||||
context(const context&) = delete;
|
|
||||||
context& operator=(const context&) = delete;
|
|
||||||
|
|
||||||
void run_frame();
|
|
||||||
|
|
||||||
private:
|
|
||||||
sol::state state_{};
|
|
||||||
std::string folder_;
|
|
||||||
std::unordered_set<std::string> loaded_scripts_;
|
|
||||||
|
|
||||||
scheduler scheduler_;
|
|
||||||
|
|
||||||
void load_script(const std::string& script);
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,77 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "engine.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
#include "../../../component/scheduler.hpp"
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
#include "../../../component/filesystem.hpp"
|
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/nt.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua::engine
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
|
||||||
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
|
||||||
|
|
||||||
auto& get_scripts()
|
|
||||||
{
|
|
||||||
static std::vector<std::unique_ptr<context>> scripts{};
|
|
||||||
return scripts;
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_scripts(const std::string& script_dir)
|
|
||||||
{
|
|
||||||
if (!utils::io::directory_exists(script_dir))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto scripts = utils::io::list_files(script_dir);
|
|
||||||
|
|
||||||
for (const auto& script : scripts)
|
|
||||||
{
|
|
||||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
|
||||||
{
|
|
||||||
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_code(const std::string& code)
|
|
||||||
{
|
|
||||||
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void start()
|
|
||||||
{
|
|
||||||
clear_converted_functions();
|
|
||||||
get_scripts().clear();
|
|
||||||
|
|
||||||
load_code(lui_common);
|
|
||||||
load_code(lui_updater);
|
|
||||||
|
|
||||||
for (const auto& path : filesystem::get_search_paths())
|
|
||||||
{
|
|
||||||
load_scripts(path + "/ui_scripts/");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
clear_converted_functions();
|
|
||||||
get_scripts().clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_frame()
|
|
||||||
{
|
|
||||||
for (auto& script : get_scripts())
|
|
||||||
{
|
|
||||||
script->run_frame();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace ui_scripting::lua::engine
|
|
||||||
{
|
|
||||||
void start();
|
|
||||||
void stop();
|
|
||||||
void run_frame();
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "error.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
void handle_error(const sol::protected_function_result& result)
|
|
||||||
{
|
|
||||||
if (!result.valid())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
printf("************** UI Script execution error **************\n");
|
|
||||||
|
|
||||||
const sol::error err = result;
|
|
||||||
printf("%s\n", err.what());
|
|
||||||
|
|
||||||
printf("****************************************************\n");
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "context.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
void handle_error(const sol::protected_function_result& result);
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
#include "std_include.hpp"
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "error.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
scheduler::scheduler(sol::state& state)
|
|
||||||
{
|
|
||||||
auto task_handle_type = state.new_usertype<task_handle>("task_handle");
|
|
||||||
|
|
||||||
task_handle_type["clear"] = [this](const task_handle& handle)
|
|
||||||
{
|
|
||||||
this->remove(handle);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::run_frame()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
this->merge_callbacks();
|
|
||||||
|
|
||||||
for (auto i = tasks.begin(); i != tasks.end();)
|
|
||||||
{
|
|
||||||
const auto now = std::chrono::high_resolution_clock::now();
|
|
||||||
const auto diff = now - i->last_call;
|
|
||||||
|
|
||||||
if (diff < i->delay)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
i->last_call = now;
|
|
||||||
|
|
||||||
if (!i->is_deleted)
|
|
||||||
{
|
|
||||||
handle_error(i->callback());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i->is_volatile || i->is_deleted)
|
|
||||||
{
|
|
||||||
i = tasks.erase(i);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::clear()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
new_tasks.clear();
|
|
||||||
tasks.clear();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const long long milliseconds,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
return this->add(callback, std::chrono::milliseconds(milliseconds), is_volatile);
|
|
||||||
}
|
|
||||||
|
|
||||||
task_handle scheduler::add(const sol::protected_function& callback, const std::chrono::milliseconds delay,
|
|
||||||
const bool is_volatile)
|
|
||||||
{
|
|
||||||
const uint64_t id = ++this->current_task_id_;
|
|
||||||
|
|
||||||
task task;
|
|
||||||
task.is_volatile = is_volatile;
|
|
||||||
task.callback = callback;
|
|
||||||
task.delay = delay;
|
|
||||||
task.last_call = std::chrono::steady_clock::now();
|
|
||||||
task.id = id;
|
|
||||||
task.is_deleted = false;
|
|
||||||
|
|
||||||
new_callbacks_.access([&task](task_list& tasks)
|
|
||||||
{
|
|
||||||
tasks.emplace_back(std::move(task));
|
|
||||||
});
|
|
||||||
|
|
||||||
return {id};
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::remove(const task_handle& handle)
|
|
||||||
{
|
|
||||||
auto mask_as_deleted = [&](task_list& tasks)
|
|
||||||
{
|
|
||||||
for (auto& task : tasks)
|
|
||||||
{
|
|
||||||
if (task.id == handle.id)
|
|
||||||
{
|
|
||||||
task.is_deleted = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
callbacks_.access(mask_as_deleted);
|
|
||||||
new_callbacks_.access(mask_as_deleted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void scheduler::merge_callbacks()
|
|
||||||
{
|
|
||||||
callbacks_.access([&](task_list& tasks)
|
|
||||||
{
|
|
||||||
new_callbacks_.access([&](task_list& new_tasks)
|
|
||||||
{
|
|
||||||
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
|
|
||||||
std::move_iterator<task_list::iterator>(new_tasks.end()));
|
|
||||||
new_tasks = {};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <utils/concurrency.hpp>
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
class context;
|
|
||||||
|
|
||||||
class task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint64_t id = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class task final : public task_handle
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::chrono::steady_clock::time_point last_call{};
|
|
||||||
sol::protected_function callback{};
|
|
||||||
std::chrono::milliseconds delay{};
|
|
||||||
bool is_volatile = false;
|
|
||||||
bool is_deleted = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
class scheduler final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
scheduler(sol::state& state);
|
|
||||||
|
|
||||||
scheduler(scheduler&&) noexcept = delete;
|
|
||||||
scheduler& operator=(scheduler&&) noexcept = delete;
|
|
||||||
|
|
||||||
scheduler(const scheduler&) = delete;
|
|
||||||
scheduler& operator=(const scheduler&) = delete;
|
|
||||||
|
|
||||||
void run_frame();
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
task_handle add(const sol::protected_function& callback, long long milliseconds, bool is_volatile);
|
|
||||||
task_handle add(const sol::protected_function& callback, std::chrono::milliseconds delay, bool is_volatile);
|
|
||||||
|
|
||||||
private:
|
|
||||||
using task_list = std::vector<task>;
|
|
||||||
utils::concurrency::container<task_list> new_callbacks_;
|
|
||||||
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
|
||||||
std::atomic_int64_t current_task_id_ = 0;
|
|
||||||
|
|
||||||
void remove(const task_handle& handle);
|
|
||||||
void merge_callbacks();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
#include <std_include.hpp>
|
|
||||||
#include "value_conversion.hpp"
|
|
||||||
#include "../execution.hpp"
|
|
||||||
#include "../../../component/ui_scripting.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
table convert_table(const sol::table& t)
|
|
||||||
{
|
|
||||||
table res{};
|
|
||||||
|
|
||||||
t.for_each([res](const sol::object& key, const sol::object& value)
|
|
||||||
{
|
|
||||||
res.set(convert(key), convert(value));
|
|
||||||
});
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value convert_function(const sol::protected_function& function)
|
|
||||||
{
|
|
||||||
const auto closure = game::hks::cclosure_Create(*game::hks::lua_state, main_function_handler, 0, 0, 0);
|
|
||||||
add_converted_function(closure, function);
|
|
||||||
|
|
||||||
game::hks::HksObject value{};
|
|
||||||
value.t = game::hks::TCFUNCTION;
|
|
||||||
value.v.cClosure = closure;
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value convert(const sol::lua_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
return {value.as<bool>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<unsigned int>())
|
|
||||||
{
|
|
||||||
return {value.as<unsigned int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<double>())
|
|
||||||
{
|
|
||||||
return {value.as<double>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<lightuserdata>())
|
|
||||||
{
|
|
||||||
return {value.as<lightuserdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<userdata>())
|
|
||||||
{
|
|
||||||
return {value.as<userdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<table>())
|
|
||||||
{
|
|
||||||
return {value.as<table>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<function>())
|
|
||||||
{
|
|
||||||
return {value.as<function>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<sol::table>())
|
|
||||||
{
|
|
||||||
return {convert_table(value.as<sol::table>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<sol::protected_function>())
|
|
||||||
{
|
|
||||||
return {convert_function(value.as<sol::protected_function>())};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value)
|
|
||||||
{
|
|
||||||
if (value.is<int>())
|
|
||||||
{
|
|
||||||
return {state, value.as<int>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<float>())
|
|
||||||
{
|
|
||||||
return {state, value.as<float>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<bool>())
|
|
||||||
{
|
|
||||||
return {state, value.as<bool>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<std::string>())
|
|
||||||
{
|
|
||||||
return {state, value.as<std::string>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<lightuserdata>())
|
|
||||||
{
|
|
||||||
return {state, value.as<lightuserdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<userdata>())
|
|
||||||
{
|
|
||||||
return {state, value.as<userdata>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<table>())
|
|
||||||
{
|
|
||||||
return {state, value.as<table>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value.is<function>())
|
|
||||||
{
|
|
||||||
return {state, value.as<function>()};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {state, sol::lua_nil};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "context.hpp"
|
|
||||||
#include "../script_value.hpp"
|
|
||||||
|
|
||||||
namespace ui_scripting::lua
|
|
||||||
{
|
|
||||||
script_value convert(const sol::lua_value& value);
|
|
||||||
sol::lua_value convert(lua_State* state, const script_value& value);
|
|
||||||
}
|
|
@ -2,9 +2,79 @@
|
|||||||
#include "execution.hpp"
|
#include "execution.hpp"
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
|
#include "../../component/ui_scripting.hpp"
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
|
hks_object::hks_object(const game::hks::HksObject& value)
|
||||||
|
{
|
||||||
|
this->assign(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
hks_object::hks_object(const hks_object& other) noexcept
|
||||||
|
{
|
||||||
|
this->operator=(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
hks_object::hks_object(hks_object&& other) noexcept
|
||||||
|
{
|
||||||
|
this->operator=(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
hks_object& hks_object::operator=(const hks_object& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->assign(other.value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hks_object& hks_object::operator=(hks_object&& other) noexcept
|
||||||
|
{
|
||||||
|
if (this != &other)
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
this->value_ = other.value_;
|
||||||
|
other.value_.t = game::hks::TNONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
hks_object::~hks_object()
|
||||||
|
{
|
||||||
|
this->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
const game::hks::HksObject& hks_object::get() const
|
||||||
|
{
|
||||||
|
return this->value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hks_object::assign(const game::hks::HksObject& value)
|
||||||
|
{
|
||||||
|
this->value_ = value;
|
||||||
|
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
|
push_value(this->value_);
|
||||||
|
this->ref_ = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||||
|
state->m_apistack.top = top;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hks_object::release()
|
||||||
|
{
|
||||||
|
if (this->ref_)
|
||||||
|
{
|
||||||
|
game::hks::hksi_luaL_unref(*game::hks::lua_state, -10000, this->ref_);
|
||||||
|
this->value_.t = game::hks::TNONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Constructors
|
* Constructors
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -32,6 +102,24 @@ namespace ui_scripting
|
|||||||
this->value_ = obj;
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const long long value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TUI64;
|
||||||
|
obj.v.i64 = value;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const unsigned long long value)
|
||||||
|
{
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TUI64;
|
||||||
|
obj.v.ui64 = value;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
script_value::script_value(const bool value)
|
script_value::script_value(const bool value)
|
||||||
{
|
{
|
||||||
game::hks::HksObject obj{};
|
game::hks::HksObject obj{};
|
||||||
@ -55,23 +143,6 @@ namespace ui_scripting
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value::script_value(const char* value)
|
|
||||||
{
|
|
||||||
game::hks::HksObject obj{};
|
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
|
||||||
if (state == nullptr)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
|
||||||
game::hks::hksi_lua_pushlstring(state, value, static_cast<unsigned int>(strlen(value)));
|
|
||||||
obj = state->m_apistack.top[-1];
|
|
||||||
|
|
||||||
this->value_ = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
script_value::script_value(const char* value, unsigned int len)
|
script_value::script_value(const char* value, unsigned int len)
|
||||||
{
|
{
|
||||||
game::hks::HksObject obj{};
|
game::hks::HksObject obj{};
|
||||||
@ -82,13 +153,19 @@ namespace ui_scripting
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
const auto top = state->m_apistack.top;
|
||||||
game::hks::hksi_lua_pushlstring(state, value, len);
|
game::hks::hksi_lua_pushlstring(state, value, len);
|
||||||
obj = state->m_apistack.top[-1];
|
obj = state->m_apistack.top[-1];
|
||||||
|
state->m_apistack.top = top;
|
||||||
|
|
||||||
this->value_ = obj;
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script_value::script_value(const char* value)
|
||||||
|
: script_value(value, static_cast<unsigned int>(strlen(value)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
script_value::script_value(const std::string& value)
|
script_value::script_value(const std::string& value)
|
||||||
: script_value(value.data(), static_cast<unsigned int>(value.size()))
|
: script_value(value.data(), static_cast<unsigned int>(value.size()))
|
||||||
{
|
{
|
||||||
@ -96,26 +173,38 @@ namespace ui_scripting
|
|||||||
|
|
||||||
script_value::script_value(const lightuserdata& value)
|
script_value::script_value(const lightuserdata& value)
|
||||||
{
|
{
|
||||||
this->value_.t = game::hks::TLIGHTUSERDATA;
|
game::hks::HksObject obj{};
|
||||||
this->value_.v.ptr = value.ptr;
|
obj.t = game::hks::TLIGHTUSERDATA;
|
||||||
|
obj.v.ptr = value.ptr;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value::script_value(const userdata& value)
|
script_value::script_value(const userdata& value)
|
||||||
{
|
{
|
||||||
this->value_.t = game::hks::TUSERDATA;
|
game::hks::HksObject obj{};
|
||||||
this->value_.v.ptr = value.ptr;
|
obj.t = game::hks::TUSERDATA;
|
||||||
|
obj.v.ptr = value.ptr;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value::script_value(const table& value)
|
script_value::script_value(const table& value)
|
||||||
{
|
{
|
||||||
this->value_.t = game::hks::TTABLE;
|
game::hks::HksObject obj{};
|
||||||
this->value_.v.ptr = value.ptr;
|
obj.t = game::hks::TTABLE;
|
||||||
|
obj.v.ptr = value.ptr;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
script_value::script_value(const function& value)
|
script_value::script_value(const function& value)
|
||||||
{
|
{
|
||||||
this->value_.t = value.type;
|
game::hks::HksObject obj{};
|
||||||
this->value_.v.ptr = value.ptr;
|
obj.t = value.type;
|
||||||
|
obj.v.ptr = value.ptr;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
@ -147,6 +236,34 @@ namespace ui_scripting
|
|||||||
return static_cast<unsigned int>(this->get_raw().v.number);
|
return static_cast<unsigned int>(this->get_raw().v.number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************
|
||||||
|
* Integer 64
|
||||||
|
**************************************************************/
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<long long>() const
|
||||||
|
{
|
||||||
|
return this->get_raw().t == game::hks::TUI64;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
bool script_value::is<unsigned long long>() const
|
||||||
|
{
|
||||||
|
return this->is<long long>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
long long script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<long long>(this->get_raw().v.ui64);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
unsigned long long script_value::get() const
|
||||||
|
{
|
||||||
|
return static_cast<unsigned long long>(this->get_raw().v.ui64);
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Boolean
|
* Boolean
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -281,7 +398,7 @@ namespace ui_scripting
|
|||||||
template <>
|
template <>
|
||||||
function script_value::get() const
|
function script_value::get() const
|
||||||
{
|
{
|
||||||
return { this->get_raw().v.cClosure, this->get_raw().t };
|
return {this->get_raw().v.cClosure, this->get_raw().t};
|
||||||
}
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
@ -290,10 +407,10 @@ namespace ui_scripting
|
|||||||
|
|
||||||
const game::hks::HksObject& script_value::get_raw() const
|
const game::hks::HksObject& script_value::get_raw() const
|
||||||
{
|
{
|
||||||
return this->value_;
|
return this->value_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool script_value::operator==(const script_value& other)
|
bool script_value::operator==(const script_value& other) const
|
||||||
{
|
{
|
||||||
if (this->get_raw().t != other.get_raw().t)
|
if (this->get_raw().t != other.get_raw().t)
|
||||||
{
|
{
|
||||||
@ -307,4 +424,26 @@ namespace ui_scripting
|
|||||||
|
|
||||||
return this->get_raw().v.native == other.get_raw().v.native;
|
return this->get_raw().v.native == other.get_raw().v.native;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arguments script_value::operator()() const
|
||||||
|
{
|
||||||
|
return this->as<function>()();
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments script_value::operator()(const arguments& arguments) const
|
||||||
|
{
|
||||||
|
return this->as<function>()(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
function_argument::function_argument(const arguments& args, const script_value& value, const int index)
|
||||||
|
: values_(args)
|
||||||
|
, value_(value)
|
||||||
|
, index_(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
function_arguments::function_arguments(const arguments& values)
|
||||||
|
: values_(values)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,92 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
class lightuserdata;
|
class lightuserdata;
|
||||||
|
class userdata_value;
|
||||||
class userdata;
|
class userdata;
|
||||||
|
class table_value;
|
||||||
class table;
|
class table;
|
||||||
class function;
|
class function;
|
||||||
|
class script_value;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
std::string get_typename()
|
||||||
|
{
|
||||||
|
auto& info = typeid(T);
|
||||||
|
|
||||||
|
if (info == typeid(std::string) ||
|
||||||
|
info == typeid(const char*))
|
||||||
|
{
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(lightuserdata))
|
||||||
|
{
|
||||||
|
return "lightuserdata";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(userdata))
|
||||||
|
{
|
||||||
|
return "userdata";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(table))
|
||||||
|
{
|
||||||
|
return "table";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(function))
|
||||||
|
{
|
||||||
|
return "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(int) ||
|
||||||
|
info == typeid(float) ||
|
||||||
|
info == typeid(unsigned int))
|
||||||
|
{
|
||||||
|
return "number";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == typeid(bool))
|
||||||
|
{
|
||||||
|
return "boolean";
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class hks_object
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
hks_object() = default;
|
||||||
|
hks_object(const game::hks::HksObject& value);
|
||||||
|
hks_object(const hks_object& other) noexcept;
|
||||||
|
hks_object(hks_object&& other) noexcept;
|
||||||
|
|
||||||
|
hks_object& operator=(const hks_object& other) noexcept;
|
||||||
|
hks_object& operator=(hks_object&& other) noexcept;
|
||||||
|
|
||||||
|
~hks_object();
|
||||||
|
|
||||||
|
const game::hks::HksObject& get() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void assign(const game::hks::HksObject& value);
|
||||||
|
void release();
|
||||||
|
|
||||||
|
game::hks::HksObject value_{game::hks::TNONE, {}};
|
||||||
|
int ref_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using arguments = std::vector<script_value>;
|
||||||
|
using event_arguments = std::unordered_map<std::string, script_value>;
|
||||||
|
|
||||||
class script_value
|
class script_value
|
||||||
{
|
{
|
||||||
@ -16,6 +96,8 @@ namespace ui_scripting
|
|||||||
|
|
||||||
script_value(int value);
|
script_value(int value);
|
||||||
script_value(unsigned int value);
|
script_value(unsigned int value);
|
||||||
|
script_value(long long value);
|
||||||
|
script_value(unsigned long long value);
|
||||||
script_value(bool value);
|
script_value(bool value);
|
||||||
|
|
||||||
script_value(float value);
|
script_value(float value);
|
||||||
@ -30,7 +112,52 @@ namespace ui_scripting
|
|||||||
script_value(const table& value);
|
script_value(const table& value);
|
||||||
script_value(const function& value);
|
script_value(const function& value);
|
||||||
|
|
||||||
bool operator==(const script_value& other);
|
template <template<class, class> class C, class T, typename TableType = table>
|
||||||
|
script_value(const C<T, std::allocator<T>>& container)
|
||||||
|
{
|
||||||
|
TableType table_{};
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
for (const auto& value : container)
|
||||||
|
{
|
||||||
|
table_.set(index++, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::hks::HksObject obj{};
|
||||||
|
obj.t = game::hks::TTABLE;
|
||||||
|
obj.v.ptr = table_.ptr;
|
||||||
|
|
||||||
|
this->value_ = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
script_value(F f)
|
||||||
|
: script_value(function(f))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const script_value& other) const;
|
||||||
|
|
||||||
|
arguments operator()() const;
|
||||||
|
arguments operator()(const arguments& arguments) const;
|
||||||
|
|
||||||
|
template<class ...T>
|
||||||
|
arguments operator()(T... arguments) const
|
||||||
|
{
|
||||||
|
return this->as<function>().call({arguments...});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t Size>
|
||||||
|
table_value operator[](const char(&key)[Size]) const
|
||||||
|
{
|
||||||
|
return {this->as<table>(), key};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = script_value>
|
||||||
|
table_value operator[](const T& key) const
|
||||||
|
{
|
||||||
|
return {this->as<table>(), key};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool is() const;
|
bool is() const;
|
||||||
@ -40,21 +167,90 @@ namespace ui_scripting
|
|||||||
{
|
{
|
||||||
if (!this->is<T>())
|
if (!this->is<T>())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Invalid type");
|
throw std::runtime_error(utils::string::va("%s expected, got %s",
|
||||||
|
typeid(T).name(), game::hks::typenames[this->get_raw().t + 2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
return get<T>();
|
return get<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
operator T() const
|
||||||
|
{
|
||||||
|
return this->as<T>();
|
||||||
|
}
|
||||||
|
|
||||||
const game::hks::HksObject& get_raw() const;
|
const game::hks::HksObject& get_raw() const;
|
||||||
|
|
||||||
private:
|
hks_object value_{};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T get() const;
|
T get() const;
|
||||||
|
|
||||||
game::hks::HksObject value_{};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using arguments = std::vector<script_value>;
|
class variadic_args : public arguments
|
||||||
using event_arguments = std::unordered_map<std::string, script_value>;
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class function_argument
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
function_argument(const arguments& args, const script_value& value, const int index);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T as() const
|
||||||
|
{
|
||||||
|
if (!this->value_.is<T>())
|
||||||
|
{
|
||||||
|
const auto hks_typename = game::hks::typenames[this->value_.get_raw().t + 2];
|
||||||
|
const auto typename_ = get_typename<T>();
|
||||||
|
|
||||||
|
throw std::runtime_error(utils::string::va("bad argument #%d (%s expected, got %s)",
|
||||||
|
this->index_ + 1, typename_.data(), hks_typename));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->value_.get<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
variadic_args as() const
|
||||||
|
{
|
||||||
|
variadic_args args{};
|
||||||
|
for (auto i = this->index_; i < this->values_.size(); i++)
|
||||||
|
{
|
||||||
|
args.push_back(this->values_[i]);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
operator T() const
|
||||||
|
{
|
||||||
|
return this->as<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
arguments values_{};
|
||||||
|
script_value value_{};
|
||||||
|
int index_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class function_arguments
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
function_arguments(const arguments& values);
|
||||||
|
|
||||||
|
function_argument operator[](const int index) const
|
||||||
|
{
|
||||||
|
if (index >= values_.size())
|
||||||
|
{
|
||||||
|
return {values_, {}, index};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {values_, values_[index], index};
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
arguments values_{};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "types.hpp"
|
#include "types.hpp"
|
||||||
#include "execution.hpp"
|
#include "execution.hpp"
|
||||||
|
#include "../../component/ui_scripting.hpp"
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
@ -73,11 +74,12 @@ namespace ui_scripting
|
|||||||
value.t = game::hks::TUSERDATA;
|
value.t = game::hks::TUSERDATA;
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
push_value(value);
|
push_value(value);
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||||
|
state->m_apistack.top = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
void userdata::release()
|
void userdata::release()
|
||||||
@ -98,6 +100,29 @@ namespace ui_scripting
|
|||||||
return get_field(*this, key);
|
return get_field(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userdata_value userdata::operator[](const script_value& key) const
|
||||||
|
{
|
||||||
|
return {*this, key};
|
||||||
|
}
|
||||||
|
|
||||||
|
userdata_value::userdata_value(const userdata& table, const script_value& key)
|
||||||
|
: userdata_(table)
|
||||||
|
, key_(key)
|
||||||
|
{
|
||||||
|
this->value_ = this->userdata_.get(key).get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void userdata_value::operator=(const script_value& value)
|
||||||
|
{
|
||||||
|
this->userdata_.set(this->key_, value);
|
||||||
|
this->value_ = value.get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool userdata_value::operator==(const script_value& value)
|
||||||
|
{
|
||||||
|
return this->userdata_.get(this->key_) == value;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Table
|
* Table
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -165,11 +190,12 @@ namespace ui_scripting
|
|||||||
value.t = game::hks::TTABLE;
|
value.t = game::hks::TTABLE;
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
push_value(value);
|
push_value(value);
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||||
|
state->m_apistack.top = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
void table::release()
|
void table::release()
|
||||||
@ -185,15 +211,57 @@ namespace ui_scripting
|
|||||||
set_field(*this, key, value);
|
set_field(*this, key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table_value table::operator[](const script_value& key) const
|
||||||
|
{
|
||||||
|
return {*this, key};
|
||||||
|
}
|
||||||
|
|
||||||
script_value table::get(const script_value& key) const
|
script_value table::get(const script_value& key) const
|
||||||
{
|
{
|
||||||
return get_field(*this, key);
|
return get_field(*this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table_value::table_value(const table& table, const script_value& key)
|
||||||
|
: table_(table)
|
||||||
|
, key_(key)
|
||||||
|
{
|
||||||
|
this->value_ = this->table_.get(key).get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_value::operator=(const script_value& value)
|
||||||
|
{
|
||||||
|
this->table_.set(this->key_, value);
|
||||||
|
this->value_ = value.get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void table_value::operator=(const table_value& value)
|
||||||
|
{
|
||||||
|
this->table_.set(this->key_, value);
|
||||||
|
this->value_ = value.get_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool table_value::operator==(const script_value& value)
|
||||||
|
{
|
||||||
|
return this->table_.get(this->key_) == value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool table_value::operator==(const table_value& value)
|
||||||
|
{
|
||||||
|
return this->table_.get(this->key_) == value;
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Function
|
* Function
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
|
|
||||||
|
function::function(game::hks::lua_function func)
|
||||||
|
{
|
||||||
|
const auto state = *game::hks::lua_state;
|
||||||
|
this->ptr = game::hks::cclosure_Create(state, func, 0, 0, 0);
|
||||||
|
this->type = game::hks::HksObjectType::TCFUNCTION;
|
||||||
|
this->add();
|
||||||
|
}
|
||||||
|
|
||||||
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
function::function(game::hks::cclosure* ptr_, game::hks::HksObjectType type_)
|
||||||
: ptr(ptr_)
|
: ptr(ptr_)
|
||||||
, type(type_)
|
, type(type_)
|
||||||
@ -254,11 +322,12 @@ namespace ui_scripting
|
|||||||
value.t = this->type;
|
value.t = this->type;
|
||||||
|
|
||||||
const auto state = *game::hks::lua_state;
|
const auto state = *game::hks::lua_state;
|
||||||
state->m_apistack.top = state->m_apistack.base;
|
const auto top = state->m_apistack.top;
|
||||||
|
|
||||||
push_value(value);
|
push_value(value);
|
||||||
|
|
||||||
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
this->ref = game::hks::hksi_luaL_ref(*game::hks::lua_state, -10000);
|
||||||
|
state->m_apistack.top = top;
|
||||||
}
|
}
|
||||||
|
|
||||||
void function::release()
|
void function::release()
|
||||||
@ -274,6 +343,16 @@ namespace ui_scripting
|
|||||||
return call_script_function(*this, arguments);
|
return call_script_function(*this, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
arguments function::operator()(const arguments& arguments) const
|
||||||
|
{
|
||||||
|
return this->call(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments function::operator()() const
|
||||||
|
{
|
||||||
|
return this->call({});
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************
|
/***************************************************************
|
||||||
* Stack
|
* Stack
|
||||||
**************************************************************/
|
**************************************************************/
|
||||||
@ -281,7 +360,6 @@ namespace ui_scripting
|
|||||||
stack::stack()
|
stack::stack()
|
||||||
{
|
{
|
||||||
this->state = *game::hks::lua_state;
|
this->state = *game::hks::lua_state;
|
||||||
this->state->m_apistack.top = this->state->m_apistack.base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void stack::save(int num_args)
|
void stack::save(int num_args)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "script_value.hpp"
|
#include "script_value.hpp"
|
||||||
|
#include "../../component/ui_scripting.hpp"
|
||||||
|
|
||||||
namespace ui_scripting
|
namespace ui_scripting
|
||||||
{
|
{
|
||||||
@ -11,6 +12,8 @@ namespace ui_scripting
|
|||||||
void* ptr;
|
void* ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class userdata_value;
|
||||||
|
|
||||||
class userdata
|
class userdata
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -27,6 +30,8 @@ namespace ui_scripting
|
|||||||
script_value get(const script_value& key) const;
|
script_value get(const script_value& key) const;
|
||||||
void set(const script_value& key, const script_value& value) const;
|
void set(const script_value& key, const script_value& value) const;
|
||||||
|
|
||||||
|
userdata_value operator[](const script_value& key) const;
|
||||||
|
|
||||||
void* ptr;
|
void* ptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -36,6 +41,19 @@ namespace ui_scripting
|
|||||||
int ref{};
|
int ref{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class userdata_value : public script_value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
userdata_value(const userdata& table, const script_value& key);
|
||||||
|
void operator=(const script_value& value);
|
||||||
|
bool operator==(const script_value& value);
|
||||||
|
private:
|
||||||
|
userdata userdata_;
|
||||||
|
script_value key_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class table_value;
|
||||||
|
|
||||||
class table
|
class table
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -53,6 +71,8 @@ namespace ui_scripting
|
|||||||
script_value get(const script_value& key) const;
|
script_value get(const script_value& key) const;
|
||||||
void set(const script_value& key, const script_value& value) const;
|
void set(const script_value& key, const script_value& value) const;
|
||||||
|
|
||||||
|
table_value operator[](const script_value& key) const;
|
||||||
|
|
||||||
game::hks::HashTable* ptr;
|
game::hks::HashTable* ptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -62,11 +82,32 @@ namespace ui_scripting
|
|||||||
int ref{};
|
int ref{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class table_value : public script_value
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
table_value(const table& table, const script_value& key);
|
||||||
|
void operator=(const script_value& value);
|
||||||
|
void operator=(const table_value& value);
|
||||||
|
bool operator==(const script_value& value);
|
||||||
|
bool operator==(const table_value& value);
|
||||||
|
private:
|
||||||
|
table table_;
|
||||||
|
script_value key_;
|
||||||
|
};
|
||||||
|
|
||||||
class function
|
class function
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
function(game::hks::lua_function);
|
||||||
function(game::hks::cclosure*, game::hks::HksObjectType);
|
function(game::hks::cclosure*, game::hks::HksObjectType);
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
function(F f)
|
||||||
|
{
|
||||||
|
this->ptr = ui_scripting::convert_function(f);
|
||||||
|
this->type = game::hks::TCFUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
function(const function& other);
|
function(const function& other);
|
||||||
function(function&& other) noexcept;
|
function(function&& other) noexcept;
|
||||||
|
|
||||||
@ -77,6 +118,16 @@ namespace ui_scripting
|
|||||||
|
|
||||||
arguments call(const arguments& arguments) const;
|
arguments call(const arguments& arguments) const;
|
||||||
|
|
||||||
|
arguments operator()(const arguments& arguments) const;
|
||||||
|
|
||||||
|
template<class ...T>
|
||||||
|
arguments operator()(T... arguments) const
|
||||||
|
{
|
||||||
|
return this->call({arguments...});
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments operator()() const;
|
||||||
|
|
||||||
game::hks::cclosure* ptr;
|
game::hks::cclosure* ptr;
|
||||||
game::hks::HksObjectType type;
|
game::hks::HksObjectType type;
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
-- SOFTWARE.
|
-- SOFTWARE.
|
||||||
--
|
--
|
||||||
|
|
||||||
local json = { _version = "0.1.2" }
|
json = { _version = "0.1.2" }
|
||||||
|
|
||||||
-------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------
|
||||||
-- Encode
|
-- Encode
|
||||||
|
@ -1,6 +1,30 @@
|
|||||||
menucallbacks = {}
|
local menucallbacks = {}
|
||||||
originalmenus = {}
|
local originalmenus = {}
|
||||||
stack = {}
|
local stack = {}
|
||||||
|
|
||||||
|
function getchildren(element)
|
||||||
|
local children = {}
|
||||||
|
local first = element:getFirstChild()
|
||||||
|
|
||||||
|
while (first) do
|
||||||
|
table.insert(children, first)
|
||||||
|
first = first:getNextSibling()
|
||||||
|
end
|
||||||
|
|
||||||
|
return children
|
||||||
|
end
|
||||||
|
|
||||||
|
function printchildtree(element, indent, last)
|
||||||
|
indent = indent or ""
|
||||||
|
|
||||||
|
print(indent .. "+- " .. element.id .. " " .. (element:getText() or ""))
|
||||||
|
indent = indent .. (last and " " or "| ")
|
||||||
|
|
||||||
|
local children = getchildren(element)
|
||||||
|
for i = 1, #children do
|
||||||
|
printchildtree(children[i], indent, i == #children)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
|
LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
|
||||||
local oncancel = stack.oncancel
|
local oncancel = stack.oncancel
|
||||||
@ -14,7 +38,7 @@ LUI.MenuBuilder.m_types_build["generic_waiting_popup_"] = function (menu, event)
|
|||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
|
||||||
popup.text = popup:getchildren()[7]
|
popup.text = popup:getLastChild():getPreviousSibling():getPreviousSibling()
|
||||||
|
|
||||||
stack = {
|
stack = {
|
||||||
ret = popup
|
ret = popup
|
||||||
@ -74,10 +98,10 @@ LUI.onmenuopen = function(name, callback)
|
|||||||
originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
|
originalmenus[name] = LUI.MenuBuilder.m_types_build[name]
|
||||||
LUI.MenuBuilder.m_types_build[name] = function(...)
|
LUI.MenuBuilder.m_types_build[name] = function(...)
|
||||||
local args = {...}
|
local args = {...}
|
||||||
local menu = originalmenus[name](table.unpack(args))
|
local menu = originalmenus[name](unpack(args))
|
||||||
|
|
||||||
for k, v in luiglobals.next, menucallbacks[name] do
|
for k, v in next, menucallbacks[name] do
|
||||||
v(menu, table.unpack(args))
|
v(menu, unpack(args))
|
||||||
end
|
end
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
@ -134,7 +158,7 @@ LUI.openpopupmenu = function(menu, args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
LUI.yesnopopup = function(data)
|
LUI.yesnopopup = function(data)
|
||||||
for k, v in luiglobals.next, data do
|
for k, v in next, data do
|
||||||
stack[k] = v
|
stack[k] = v
|
||||||
end
|
end
|
||||||
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
|
LUI.FlowManager.RequestPopupMenu(nil, "generic_yes_no_popup_")
|
||||||
@ -142,21 +166,9 @@ LUI.yesnopopup = function(data)
|
|||||||
end
|
end
|
||||||
|
|
||||||
LUI.confirmationpopup = function(data)
|
LUI.confirmationpopup = function(data)
|
||||||
for k, v in luiglobals.next, data do
|
for k, v in next, data do
|
||||||
stack[k] = v
|
stack[k] = v
|
||||||
end
|
end
|
||||||
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
|
LUI.FlowManager.RequestPopupMenu(nil, "generic_confirmation_popup_")
|
||||||
return stack.ret
|
return stack.ret
|
||||||
end
|
end
|
||||||
|
|
||||||
function userdata_:getchildren()
|
|
||||||
local children = {}
|
|
||||||
local first = self:getFirstChild()
|
|
||||||
|
|
||||||
while (first) do
|
|
||||||
table.insert(children, first)
|
|
||||||
first = first:getNextSibling()
|
|
||||||
end
|
|
||||||
|
|
||||||
return children
|
|
||||||
end
|
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
updatecancelled = false
|
if (not Engine.InFrontend()) then
|
||||||
taskinterval = 100
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
updatecancelled = false
|
||||||
updater.cancelupdate()
|
updater.cancelupdate()
|
||||||
|
|
||||||
function startupdatecheck(popup, autoclose)
|
function startupdatecheck(popup, autoclose)
|
||||||
updatecancelled = false
|
Engine.GetLuiRoot():registerEventHandler("update_check_done", function(element, event)
|
||||||
|
if (updatecancelled) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
local callback = function()
|
|
||||||
if (not updater.getupdatecheckstatus()) then
|
if (not updater.getupdatecheckstatus()) then
|
||||||
if (autoclose) then
|
if (autoclose) then
|
||||||
LUI.FlowManager.RequestLeaveMenu(popup)
|
LUI.FlowManager.RequestLeaveMenu(popup)
|
||||||
@ -38,23 +42,18 @@ function startupdatecheck(popup, autoclose)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
end
|
end)
|
||||||
|
|
||||||
updater.startupdatecheck()
|
updater.startupdatecheck()
|
||||||
createtask({
|
|
||||||
done = updater.isupdatecheckdone,
|
|
||||||
cancelled = isupdatecancelled,
|
|
||||||
callback = callback,
|
|
||||||
interval = taskinterval
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function startupdatedownload(popup, autoclose)
|
function startupdatedownload(popup, autoclose)
|
||||||
updater.startupdatedownload()
|
|
||||||
|
|
||||||
local textupdate = nil
|
local textupdate = nil
|
||||||
local previousfile = nil
|
local previousfile = nil
|
||||||
textupdate = game:oninterval(function()
|
local timer = LUI.UITimer.new(10, "update_file")
|
||||||
|
|
||||||
|
popup:addElement(timer)
|
||||||
|
popup:registerEventHandler("update_file", function()
|
||||||
local file = updater.getcurrentfile()
|
local file = updater.getcurrentfile()
|
||||||
if (file == previousfile) then
|
if (file == previousfile) then
|
||||||
return
|
return
|
||||||
@ -62,10 +61,14 @@ function startupdatedownload(popup, autoclose)
|
|||||||
|
|
||||||
file = previousfile
|
file = previousfile
|
||||||
popup.text:setText("Downloading file " .. updater.getcurrentfile() .. "...")
|
popup.text:setText("Downloading file " .. updater.getcurrentfile() .. "...")
|
||||||
end, 10)
|
end)
|
||||||
|
|
||||||
local callback = function()
|
Engine.GetLuiRoot():registerEventHandler("update_done", function(element, event)
|
||||||
textupdate:clear()
|
timer:close()
|
||||||
|
|
||||||
|
if (updatecancelled) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
if (not updater.getupdatedownloadstatus()) then
|
if (not updater.getupdatedownloadstatus()) then
|
||||||
if (autoclose) then
|
if (autoclose) then
|
||||||
@ -95,14 +98,9 @@ function startupdatedownload(popup, autoclose)
|
|||||||
if (autoclose) then
|
if (autoclose) then
|
||||||
LUI.FlowManager.RequestLeaveMenu(popup)
|
LUI.FlowManager.RequestLeaveMenu(popup)
|
||||||
end
|
end
|
||||||
end
|
end)
|
||||||
|
|
||||||
createtask({
|
updater.startupdatedownload()
|
||||||
done = updater.isupdatedownloaddone,
|
|
||||||
cancelled = isupdatecancelled,
|
|
||||||
callback = callback,
|
|
||||||
interval = taskinterval
|
|
||||||
})
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function updaterpopup(oncancel)
|
function updaterpopup(oncancel)
|
||||||
@ -129,10 +127,6 @@ function createtask(data)
|
|||||||
return interval
|
return interval
|
||||||
end
|
end
|
||||||
|
|
||||||
function isupdatecancelled()
|
|
||||||
return updatecancelled
|
|
||||||
end
|
|
||||||
|
|
||||||
function tryupdate(autoclose)
|
function tryupdate(autoclose)
|
||||||
updatecancelled = false
|
updatecancelled = false
|
||||||
local popup = updaterpopup(function()
|
local popup = updaterpopup(function()
|
||||||
@ -149,10 +143,13 @@ function tryautoupdate()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if (not updater.gethastriedupdate()) then
|
if (not updater.gethastriedupdate()) then
|
||||||
game:ontimeout(function()
|
local timer = LUI.UITimer.new(100, "tryupdate")
|
||||||
|
Engine.GetLuiRoot():addElement(timer)
|
||||||
|
Engine.GetLuiRoot():registerEventHandler("tryupdate", function()
|
||||||
|
timer:close()
|
||||||
updater.sethastriedupdate(true)
|
updater.sethastriedupdate(true)
|
||||||
tryupdate(true)
|
tryupdate(true)
|
||||||
end, 100)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user