Use builtin LUI event handler + fixes
This commit is contained in:
parent
cc4c8b242d
commit
d88a48b666
@ -1,4 +1,3 @@
|
||||
@echo off
|
||||
git submodule update --init --recursive
|
||||
tools\premake5 %* vs2022
|
||||
pause
|
@ -6,6 +6,7 @@
|
||||
#include "game_console.hpp"
|
||||
#include "gui.hpp"
|
||||
#include "game/ui_scripting/lua/engine.hpp"
|
||||
#include "game/ui_scripting/execution.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
@ -25,7 +26,11 @@ namespace input
|
||||
|
||||
void cl_char_event_stub(const int local_client_num, const int key)
|
||||
{
|
||||
ui_scripting::lua::engine::ui_event("char", {key});
|
||||
ui_scripting::notify("keypress",
|
||||
{
|
||||
{"keynum", key},
|
||||
{"key", game::Key_KeynumToString(key, 0, 1)},
|
||||
});
|
||||
|
||||
if (!game_console::console_char_event(local_client_num, key))
|
||||
{
|
||||
@ -42,7 +47,11 @@ namespace input
|
||||
|
||||
void cl_key_event_stub(const int local_client_num, const int key, const int down)
|
||||
{
|
||||
ui_scripting::lua::engine::ui_event("key", {key, down});
|
||||
ui_scripting::notify(down ? "keydown" : "keyup",
|
||||
{
|
||||
{"keynum", key},
|
||||
{"key", game::Key_KeynumToString(key, 0, 1)},
|
||||
});
|
||||
|
||||
if (!game_console::console_key_event(local_client_num, key, down))
|
||||
{
|
||||
@ -64,7 +73,6 @@ namespace input
|
||||
return;
|
||||
}
|
||||
|
||||
ui_scripting::lua::engine::ui_event("mousemove", {x, y});
|
||||
cl_mouse_move_hook.invoke<void>(local_client_num, x, y);
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,9 @@ namespace ui_scripting
|
||||
utils::hook::detour hks_allocator_hook;
|
||||
utils::hook::detour lui_error_hook;
|
||||
utils::hook::detour hksi_hks_error_hook;
|
||||
utils::hook::detour hks_frame_hook;
|
||||
|
||||
bool error_hook_enabled = false;
|
||||
int error_hook_enabled = 0;
|
||||
|
||||
void hksi_lual_error_stub(game::hks::lua_State* s, const char* fmt, ...)
|
||||
{
|
||||
@ -107,6 +108,15 @@ namespace ui_scripting
|
||||
|
||||
return hks_allocator_hook.invoke<void*>(userData, oldMemory, oldSize, newSize);
|
||||
}
|
||||
|
||||
void hks_frame_stub()
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (state)
|
||||
{
|
||||
ui_scripting::lua::engine::run_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main_function_handler(game::hks::lua_State* state)
|
||||
@ -158,17 +168,12 @@ namespace ui_scripting
|
||||
|
||||
void enable_error_hook()
|
||||
{
|
||||
error_hook_enabled = true;
|
||||
error_hook_enabled++;
|
||||
}
|
||||
|
||||
void disable_error_hook()
|
||||
{
|
||||
error_hook_enabled = false;
|
||||
}
|
||||
|
||||
void notify(const event& e)
|
||||
{
|
||||
lua::engine::notify(e);
|
||||
error_hook_enabled--;
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
@ -177,7 +182,7 @@ namespace ui_scripting
|
||||
|
||||
void post_unpack() override
|
||||
{
|
||||
scheduler::loop(ui_scripting::lua::engine::run_frame, scheduler::pipeline::lui);
|
||||
hks_frame_hook.create(0x140327880, hks_frame_stub);
|
||||
hks_start_hook.create(0x140328BE0, hks_start_stub);
|
||||
hks_shutdown_hook.create(0x1403203B0, hks_shutdown_stub);
|
||||
hksi_lual_error_hook.create(0x1402E3E40, hksi_lual_error_stub);
|
||||
|
@ -10,6 +10,4 @@ namespace ui_scripting
|
||||
|
||||
void enable_error_hook();
|
||||
void disable_error_hook();
|
||||
|
||||
void notify(const event& e);
|
||||
}
|
||||
|
@ -86,6 +86,10 @@ namespace game
|
||||
WEAK symbol<const char*(int, int, int)> Key_KeynumToString{0x1403D32D0};
|
||||
|
||||
WEAK symbol<void(int clientNum, const char* menu, int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x1405F0EE0};
|
||||
WEAK symbol<bool(int clientNum, const char* name, hks::lua_State* s)> LUI_BeginEvent{0x1403155E0};
|
||||
WEAK symbol<void(hks::lua_State* s)> LUI_EndEvent{0x140316890};
|
||||
WEAK symbol<void()> LUI_EnterCriticalSection{0x140316980};
|
||||
WEAK symbol<void()> LUI_LeaveCriticalSection{0x14031BC20};
|
||||
WEAK symbol<bool(int clientNum, const char* menu)> Menu_IsMenuOpenAndVisible{0x1405EE1A0};
|
||||
|
||||
WEAK symbol<Material*(const char* material)> Material_RegisterHandle{0x140759BA0};
|
||||
@ -135,6 +139,8 @@ namespace game
|
||||
WEAK symbol<bool()> Sys_IsDatabaseReady2{0x1405A9FE0};
|
||||
WEAK symbol<int()> Sys_Milliseconds{0x140650720};
|
||||
WEAK symbol<bool()> Sys_IsMainThread{0x1405AA020};
|
||||
WEAK symbol<void(int critSec)> Sys_EnterCriticalSection{0x140624240};
|
||||
WEAK symbol<void(int critSec)> Sys_LeaveCriticalSection{0x1406242C0};
|
||||
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x1405A2930};
|
||||
WEAK symbol<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x140606080};
|
||||
@ -202,5 +208,8 @@ namespace game
|
||||
int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x1402C84B0};
|
||||
WEAK symbol<int(lua_State* s, int t)> hksi_luaL_ref{0x1402E4520};
|
||||
WEAK symbol<void(lua_State* s, int t, int ref)> hksi_luaL_unref{0x1402DCE50};
|
||||
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<void(lua_State* s, HksObject* lfp)> closePendingUpvalues{0x1402CBAD0};
|
||||
}
|
||||
}
|
@ -37,38 +37,79 @@ namespace ui_scripting
|
||||
return values;
|
||||
}
|
||||
|
||||
bool notify(const std::string& name, const event_arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
if (!state)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto _1 = gsl::finally(game::LUI_LeaveCriticalSection);
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
stack stack;
|
||||
push_value(function);
|
||||
for (auto i = arguments.begin(); i != arguments.end(); ++i)
|
||||
{
|
||||
push_value(*i);
|
||||
}
|
||||
|
||||
const auto num_args = static_cast<int>(arguments.size());
|
||||
stack.save(num_args + 1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
|
||||
try
|
||||
{
|
||||
game::hks::vm_call_internal(state, static_cast<int>(arguments.size()), -1, 0);
|
||||
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)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error executing script function: ") + e.what());
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error executing script function: "s + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
stack stack;
|
||||
push_value(key);
|
||||
stack.save(1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
@ -85,16 +126,18 @@ namespace ui_scripting
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error getting userdata field: ") + e.what());
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error getting userdata field: "s + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
script_value get_field(const table& self, const script_value& key)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
stack stack;
|
||||
push_value(key);
|
||||
stack.save(1);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
@ -111,14 +154,17 @@ namespace ui_scripting
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error getting table field: ") + e.what());
|
||||
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)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
stack stack;
|
||||
stack.save(0);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
@ -133,14 +179,17 @@ namespace ui_scripting
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error setting userdata field: ") + e.what());
|
||||
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)
|
||||
{
|
||||
const auto state = *game::hks::lua_state;
|
||||
state->m_apistack.top = state->m_apistack.base;
|
||||
|
||||
stack stack;
|
||||
stack.save(0);
|
||||
|
||||
const auto _1 = gsl::finally(&disable_error_hook);
|
||||
enable_error_hook();
|
||||
@ -155,7 +204,8 @@ namespace ui_scripting
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
throw std::runtime_error(std::string("Error setting table field: ") + e.what());
|
||||
stack.fix();
|
||||
throw std::runtime_error("Error setting table field: "s + e.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ namespace ui_scripting
|
||||
script_value get_return_value(int offset);
|
||||
arguments get_return_values(int count);
|
||||
|
||||
bool notify(const std::string& name, const event_arguments& arguments);
|
||||
|
||||
arguments call_script_function(const function& function, const arguments& arguments);
|
||||
|
||||
script_value get_field(const userdata& self, const script_value& key);
|
||||
|
@ -210,7 +210,7 @@ namespace ui_scripting::lua
|
||||
};
|
||||
}
|
||||
|
||||
void setup_game_type(sol::state& state, event_handler& handler, scheduler& scheduler)
|
||||
void setup_game_type(sol::state& state, scheduler& scheduler)
|
||||
{
|
||||
struct game
|
||||
{
|
||||
@ -246,28 +246,6 @@ namespace ui_scripting::lua
|
||||
return scheduler.add(callback, milliseconds, false);
|
||||
};
|
||||
|
||||
game_type["onnotify"] = [&handler](const game&, const std::string& event,
|
||||
const event_callback& callback)
|
||||
{
|
||||
event_listener listener{};
|
||||
listener.callback = callback;
|
||||
listener.event = event;
|
||||
listener.is_volatile = false;
|
||||
|
||||
return handler.add_event_listener(std::move(listener));
|
||||
};
|
||||
|
||||
game_type["onnotifyonce"] = [&handler](const game&, const std::string& event,
|
||||
const event_callback& callback)
|
||||
{
|
||||
event_listener listener{};
|
||||
listener.callback = callback;
|
||||
listener.event = event;
|
||||
listener.is_volatile = true;
|
||||
|
||||
return handler.add_event_listener(std::move(listener));
|
||||
};
|
||||
|
||||
game_type["isingame"] = []()
|
||||
{
|
||||
return ::game::CL_IsCgameInitialized() && ::game::g_entities[0].client;
|
||||
@ -478,7 +456,7 @@ namespace ui_scripting::lua
|
||||
};
|
||||
}
|
||||
|
||||
void setup_lui_types(sol::state& state, event_handler& handler, scheduler& scheduler)
|
||||
void setup_lui_types(sol::state& state)
|
||||
{
|
||||
auto userdata_type = state.new_usertype<userdata>("userdata_");
|
||||
|
||||
@ -494,15 +472,15 @@ namespace ui_scripting::lua
|
||||
);
|
||||
|
||||
userdata_type[sol::meta_function::index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const std::string& name)
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, userdata.get(name));
|
||||
return convert(s, userdata.get(convert({s, key})));
|
||||
};
|
||||
|
||||
userdata_type[sol::meta_function::new_index] = [](const userdata& userdata, const sol::this_state s,
|
||||
const std::string& name, const sol::lua_value& value)
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
userdata.set(name, convert({s, value}));
|
||||
userdata.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto table_type = state.new_usertype<table>("table_");
|
||||
@ -519,27 +497,27 @@ namespace ui_scripting::lua
|
||||
);
|
||||
|
||||
table_type["get"] = [](const table& table, const sol::this_state s,
|
||||
const std::string& name)
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(name));
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type["set"] = [](const table& table, const sol::this_state s,
|
||||
const std::string& name, const sol::lua_value& value)
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(name, convert({s, value}));
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::index] = [](const table& table, const sol::this_state s,
|
||||
const std::string& name)
|
||||
const sol::lua_value& key)
|
||||
{
|
||||
return convert(s, table.get(name));
|
||||
return convert(s, table.get(convert({s, key})));
|
||||
};
|
||||
|
||||
table_type[sol::meta_function::new_index] = [](const table& table, const sol::this_state s,
|
||||
const std::string& name, const sol::lua_value& value)
|
||||
const sol::lua_value& key, const sol::lua_value& value)
|
||||
{
|
||||
table.set(name, convert({s, value}));
|
||||
table.set(convert({s, key}), convert({s, value}));
|
||||
};
|
||||
|
||||
auto function_type = state.new_usertype<function>("function_");
|
||||
@ -598,8 +576,6 @@ namespace ui_scripting::lua
|
||||
|
||||
context::context(std::string data, script_type type)
|
||||
: scheduler_(state_)
|
||||
, event_handler_(state_)
|
||||
|
||||
{
|
||||
this->state_.open_libraries(sol::lib::base,
|
||||
sol::lib::package,
|
||||
@ -612,8 +588,8 @@ namespace ui_scripting::lua
|
||||
setup_io(this->state_);
|
||||
setup_json(this->state_);
|
||||
setup_vector_type(this->state_);
|
||||
setup_game_type(this->state_, this->event_handler_, this->scheduler_);
|
||||
setup_lui_types(this->state_, this->event_handler_, this->scheduler_);
|
||||
setup_game_type(this->state_, this->scheduler_);
|
||||
setup_lui_types(this->state_);
|
||||
|
||||
if (type == script_type::file)
|
||||
{
|
||||
@ -650,7 +626,6 @@ namespace ui_scripting::lua
|
||||
{
|
||||
this->state_.collect_garbage();
|
||||
this->scheduler_.clear();
|
||||
this->event_handler_.clear();
|
||||
this->state_ = {};
|
||||
}
|
||||
|
||||
@ -660,12 +635,6 @@ namespace ui_scripting::lua
|
||||
this->state_.collect_garbage();
|
||||
}
|
||||
|
||||
void context::notify(const event& e)
|
||||
{
|
||||
this->scheduler_.dispatch(e);
|
||||
this->event_handler_.dispatch(e);
|
||||
}
|
||||
|
||||
void context::load_script(const std::string& script)
|
||||
{
|
||||
if (!this->loaded_scripts_.emplace(script).second)
|
||||
|
@ -41,7 +41,6 @@ namespace ui_scripting::lua
|
||||
std::unordered_set<std::string> loaded_scripts_;
|
||||
|
||||
scheduler scheduler_;
|
||||
event_handler event_handler_;
|
||||
|
||||
void load_script(const std::string& script);
|
||||
};
|
||||
|
@ -17,27 +17,6 @@ namespace ui_scripting::lua::engine
|
||||
const auto lui_common = utils::nt::load_resource(LUI_COMMON);
|
||||
const auto lui_updater = utils::nt::load_resource(LUI_UPDATER);
|
||||
|
||||
void handle_key_event(const int key, const int down)
|
||||
{
|
||||
event event;
|
||||
event.name = down
|
||||
? "keydown"
|
||||
: "keyup";
|
||||
event.arguments = {key};
|
||||
|
||||
engine::notify(event);
|
||||
}
|
||||
|
||||
void handle_char_event(const int key)
|
||||
{
|
||||
std::string key_str = {(char)key};
|
||||
event event;
|
||||
event.name = "keypress";
|
||||
event.arguments = {key_str};
|
||||
|
||||
engine::notify(event);
|
||||
}
|
||||
|
||||
auto& get_scripts()
|
||||
{
|
||||
static std::vector<std::unique_ptr<context>> scripts{};
|
||||
@ -88,27 +67,6 @@ namespace ui_scripting::lua::engine
|
||||
get_scripts().clear();
|
||||
}
|
||||
|
||||
void ui_event(const std::string& type, const std::vector<int>& arguments)
|
||||
{
|
||||
if (type == "key")
|
||||
{
|
||||
handle_key_event(arguments[0], arguments[1]);
|
||||
}
|
||||
|
||||
if (type == "char")
|
||||
{
|
||||
handle_char_event(arguments[0]);
|
||||
}
|
||||
}
|
||||
|
||||
void notify(const event& e)
|
||||
{
|
||||
for (auto& script : get_scripts())
|
||||
{
|
||||
script->notify(e);
|
||||
}
|
||||
}
|
||||
|
||||
void run_frame()
|
||||
{
|
||||
for (auto& script : get_scripts())
|
||||
|
@ -6,8 +6,5 @@ namespace ui_scripting::lua::engine
|
||||
{
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void ui_event(const std::string&, const std::vector<int>&);
|
||||
void notify(const event& e);
|
||||
void run_frame();
|
||||
}
|
||||
|
@ -56,4 +56,5 @@ namespace ui_scripting
|
||||
};
|
||||
|
||||
using arguments = std::vector<script_value>;
|
||||
using event_arguments = std::unordered_map<std::string, script_value>;
|
||||
}
|
||||
|
@ -273,4 +273,38 @@ namespace ui_scripting
|
||||
{
|
||||
return call_script_function(*this, arguments);
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
* Stack
|
||||
**************************************************************/
|
||||
|
||||
stack::stack()
|
||||
{
|
||||
this->state = *game::hks::lua_state;
|
||||
this->state->m_apistack.top = this->state->m_apistack.base;
|
||||
}
|
||||
|
||||
void stack::save(int num_args)
|
||||
{
|
||||
this->num_args_ = num_args;
|
||||
this->num_calls_ = state->m_numberOfCCalls;
|
||||
this->base_bottom_ = state->m_apistack.base - state->m_apistack.bottom;
|
||||
this->top_bottom_ = state->m_apistack.top - state->m_apistack.bottom;
|
||||
this->callstack_ = state->m_callStack.m_current - state->m_callStack.m_records;
|
||||
}
|
||||
|
||||
void stack::fix()
|
||||
{
|
||||
this->state->m_numberOfCCalls = this->num_calls_;
|
||||
|
||||
game::hks::closePendingUpvalues(this->state, &this->state->m_apistack.bottom[this->top_bottom_ - this->num_args_]);
|
||||
this->state->m_callStack.m_current = &this->state->m_callStack.m_records[this->callstack_];
|
||||
|
||||
this->state->m_apistack.base = &this->state->m_apistack.bottom[this->base_bottom_];
|
||||
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ - static_cast<uint64_t>(this->num_args_ + 1)];
|
||||
|
||||
this->state->m_apistack.bottom[this->top_bottom_].t = this->state->m_apistack.top[-1].t;
|
||||
this->state->m_apistack.bottom[this->top_bottom_].v.ptr = this->state->m_apistack.top[-1].v.ptr;
|
||||
this->state->m_apistack.top = &this->state->m_apistack.bottom[this->top_bottom_ + 1];
|
||||
}
|
||||
}
|
@ -86,4 +86,28 @@ namespace ui_scripting
|
||||
|
||||
int ref{};
|
||||
};
|
||||
|
||||
class stack final
|
||||
{
|
||||
public:
|
||||
stack();
|
||||
|
||||
void save(int num_args);
|
||||
void fix();
|
||||
|
||||
stack(stack&&) = delete;
|
||||
stack(const stack&) = delete;
|
||||
stack& operator=(stack&&) = delete;
|
||||
stack& operator=(const stack&) = delete;
|
||||
|
||||
private:
|
||||
game::hks::lua_State* state;
|
||||
|
||||
int num_args_;
|
||||
int num_calls_;
|
||||
|
||||
uint64_t base_bottom_;
|
||||
uint64_t top_bottom_;
|
||||
uint64_t callstack_;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user