diff --git a/src/client/component/input.cpp b/src/client/component/input.cpp index a10ac12a..c1b39b9d 100644 --- a/src/client/component/input.cpp +++ b/src/client/component/input.cpp @@ -4,6 +4,7 @@ #include "game/game.hpp" #include "game_console.hpp" +#include "game/ui_scripting/execution.hpp" #include @@ -16,6 +17,12 @@ namespace input void cl_char_event_stub(const int local_client_num, const int key) { + ui_scripting::notify("keypress", + { + {"keynum", key}, + {"key", game::Key_KeynumToString(key, 0, 1)}, + }); + if (!game_console::console_char_event(local_client_num, key)) { return; @@ -26,6 +33,12 @@ namespace input void cl_key_event_stub(const int local_client_num, const int key, const int 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)) { return; diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index 974cbab9..6653b555 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -153,6 +153,8 @@ namespace game int a3, int a4, unsigned int a5)> LUI_OpenMenu{0x14039D5F0, 0x1404CD210}; WEAK symbol LUI_BeginEvent{0x1400D27F0, 0x140161A00}; WEAK symbol LUI_EndEvent{0x1400D3A80, 0x140162CD0}; + WEAK symbol LUI_EnterCriticalSection{0x1400D3B70, 0x140162DC0}; + WEAK symbol LUI_LeaveCriticalSection{0x1400D8DB0, 0x140168150}; WEAK symbol Menu_IsMenuOpenAndVisible{0x1404709C0, 0x1404C7320}; @@ -273,5 +275,6 @@ namespace game int internal_, int profilerTreatClosureAsFunc)> cclosure_Create{0x14008B5D0, 0x14011B540}; WEAK symbol hksi_luaL_ref{0x1400A64D0, 0x140136D30}; WEAK symbol hksi_luaL_unref{0x14009EF10, 0x14012F610}; + WEAK symbol closePendingUpvalues{0x14008EA00, 0x14011E970}; } } diff --git a/src/client/game/ui_scripting/execution.cpp b/src/client/game/ui_scripting/execution.cpp index cc8df795..716fe1ca 100644 --- a/src/client/game/ui_scripting/execution.cpp +++ b/src/client/game/ui_scripting/execution.cpp @@ -40,53 +40,53 @@ namespace ui_scripting bool notify(const std::string& name, const event_arguments& arguments) { const auto state = *game::hks::lua_state; - if (!game::LUI_BeginEvent(0, name.data(), state)) + if (!state) { return false; } - - const auto _1 = gsl::finally(&disable_error_hook); - enable_error_hook(); - const auto top = state->m_apistack.top; + const auto _1 = gsl::finally(game::LUI_LeaveCriticalSection); + game::LUI_EnterCriticalSection(); + try { - const auto event = get_return_value(0).as(); + const auto globals = table((*::game::hks::lua_state)->globals.v.table); + const auto engine = globals.get("Engine").as
(); + const auto root = engine.get("GetLuiRoot").as().call({})[0].as(); + const auto process_event = root.get("processEvent").as(); + + table event{}; + event.set("name", name); for (const auto& arg : arguments) { event.set(arg.first, arg.second); } - } - catch (...) - { - } - state->m_apistack.top = top; - - try - { - game::LUI_EndEvent(state); + process_event.call({root, event}); + return true; } catch (const std::exception& e) { - throw std::runtime_error(std::string("Error while processing event: ") + e.what()); + printf("Error processing event '%s' %s\n", name.data(), e.what()); + return false; } - - return true; } 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(arguments.size()); + stack.save(num_args + 1); + const auto _1 = gsl::finally(&disable_error_hook); enable_error_hook(); @@ -98,6 +98,7 @@ namespace ui_scripting } catch (const std::exception& e) { + stack.fix(); throw std::runtime_error(std::string("Error executing script function: ") + e.what()); } } @@ -105,9 +106,10 @@ namespace ui_scripting 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(); @@ -124,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(); @@ -150,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(); @@ -172,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(); @@ -194,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()); } } } diff --git a/src/client/game/ui_scripting/types.cpp b/src/client/game/ui_scripting/types.cpp index 37032b1b..66e8d497 100644 --- a/src/client/game/ui_scripting/types.cpp +++ b/src/client/game/ui_scripting/types.cpp @@ -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(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]; + } } diff --git a/src/client/game/ui_scripting/types.hpp b/src/client/game/ui_scripting/types.hpp index 1924407f..bc2f7216 100644 --- a/src/client/game/ui_scripting/types.hpp +++ b/src/client/game/ui_scripting/types.hpp @@ -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_; + }; }