diff --git a/src/lua/bindings/event.hpp b/src/lua/bindings/event.hpp index 10b87384..aaadf970 100644 --- a/src/lua/bindings/event.hpp +++ b/src/lua/bindings/event.hpp @@ -88,7 +88,7 @@ namespace lua::event // Param: menu_event: menu_event: The menu_event that we want to respond to. // Param: func: function: The function that will be called. // Register a function that will be called each time the corresponding menu_event is triggered. - static void register_handler(const menu_event& menu_event, sol::function func, sol::this_state state) + static void register_handler(const menu_event& menu_event, sol::protected_function func, sol::this_state state) { const auto module = sol::state_view(state)["!this"].get(); diff --git a/src/lua/bindings/gui.hpp b/src/lua/bindings/gui.hpp index f4dea41c..800738bd 100644 --- a/src/lua/bindings/gui.hpp +++ b/src/lua/bindings/gui.hpp @@ -166,7 +166,7 @@ namespace lua::gui // Param: name: string: Text written inside the button. // Param: callback: function: function that will be called when the button is clicked. // Add a button to the gui tab. - std::shared_ptr add_button(const std::string& name, sol::function callback, sol::this_state state) + std::shared_ptr add_button(const std::string& name, sol::protected_function callback, sol::this_state state) { auto element = std::make_shared(name, callback); add_element(state, m_tab_hash, element); diff --git a/src/lua/bindings/gui/button.cpp b/src/lua/bindings/gui/button.cpp index 763f0dc9..750bbf20 100644 --- a/src/lua/bindings/gui/button.cpp +++ b/src/lua/bindings/gui/button.cpp @@ -5,7 +5,7 @@ namespace lua::gui { - button::button(std::string text, sol::function callback) : + button::button(std::string text, sol::protected_function callback) : base_text_element(text), m_callback(callback) { diff --git a/src/lua/bindings/gui/button.hpp b/src/lua/bindings/gui/button.hpp index b61046f2..6dce6ff4 100644 --- a/src/lua/bindings/gui/button.hpp +++ b/src/lua/bindings/gui/button.hpp @@ -10,11 +10,11 @@ namespace lua::gui // Class representing a gui button. class button : public base_text_element { - sol::function m_callback; + sol::protected_function m_callback; bool m_execute_in_fiber_pool = true; public: - button(std::string text, sol::function callback); + button(std::string text, sol::protected_function callback); void draw() override; }; diff --git a/src/lua/bindings/script.hpp b/src/lua/bindings/script.hpp index b302ef9f..6353d121 100644 --- a/src/lua/bindings/script.hpp +++ b/src/lua/bindings/script.hpp @@ -70,14 +70,13 @@ namespace lua::script // ENTITY.DELETE_ENTITY(spawnedVehicle) // end) // ``` - static void register_looped(const std::string& name, sol::function func_, sol::this_state state) + static void register_looped(const std::string& name, sol::protected_function func_, sol::this_state state) { auto module = sol::state_view(state)["!this"].get(); std::unique_ptr lua_script = std::make_unique( [func_, state]() mutable { - - sol::thread t = sol::thread::create(state); + sol::thread t = sol::thread::create(state); sol::coroutine func = sol::coroutine(t.state(), func_); while (big::g_running) @@ -97,8 +96,7 @@ namespace lua::script } } }, - name - ); + name); const auto registered_script = big::g_script_mgr.add_script(std::move(lua_script)); @@ -115,25 +113,25 @@ namespace lua::script // script.run_in_fiber(function (script) // -- sleep until next game frame // script:yield() - // + // // local ModelHash = joaat("adder") - // if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end - // STREAMING.REQUEST_MODEL(ModelHash) -- Request the model - // while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load - // script:yield() - // end - // local myPed = PLAYER.PLAYER_PED_ID() - // local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true) - // -- Spawns a networked vehicle on your current coords - // local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false) - // -- removes model from game memory as we no longer need it - // STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash) + // if not STREAMING.IS_MODEL_IN_CDIMAGE(ModelHash) then return end + // STREAMING.REQUEST_MODEL(ModelHash) -- Request the model + // while not STREAMING.HAS_MODEL_LOADED(ModelHash) do -- Waits for the model to load + // script:yield() + // end + // local myPed = PLAYER.PLAYER_PED_ID() + // local myCoords = ENTITY.GET_ENTITY_COORDS(myPed, true) + // -- Spawns a networked vehicle on your current coords + // local spawnedVehicle = VEHICLE.CREATE_VEHICLE(ModelHash, myCoords.x, myCoords.y, myCoords.z, ENTITY.GET_ENTITY_HEADING(myPed), true, false) + // -- removes model from game memory as we no longer need it + // STREAMING.SET_MODEL_AS_NO_LONGER_NEEDED(ModelHash) // -- sleep for 2s - // script:sleep(2000) - // ENTITY.DELETE_ENTITY(spawnedVehicle) + // script:sleep(2000) + // ENTITY.DELETE_ENTITY(spawnedVehicle) // end) // ``` - static void run_in_fiber(sol::function func_, sol::this_state state) + static void run_in_fiber(sol::protected_function func_, sol::this_state state) { auto module = sol::state_view(state)["!this"].get(); diff --git a/src/lua/lua_manager.cpp b/src/lua/lua_manager.cpp index be102100..cb2ea3af 100644 --- a/src/lua/lua_manager.cpp +++ b/src/lua/lua_manager.cpp @@ -157,7 +157,8 @@ namespace big void lua_manager::handle_error(const sol::error& error, const sol::state_view& state) { - LOG(WARNING) << state["!module_name"].get() << ": " << error.what(); + LOG(FATAL) << state["!module_name"].get() << ": " << error.what(); + Logger::FlushQueue(); } void lua_manager::load_all_modules() diff --git a/src/lua/lua_module.cpp b/src/lua/lua_module.cpp index cb31ecf3..ee4bb90d 100644 --- a/src/lua/lua_module.cpp +++ b/src/lua/lua_module.cpp @@ -18,30 +18,41 @@ namespace big { - inline int exception_handler(lua_State* L, sol::optional exception, std::string_view what) + // https://sol2.readthedocs.io/en/latest/exceptions.html + int exception_handler(lua_State* L, sol::optional maybe_exception, sol::string_view description) { - if (exception) - LOG(WARNING) << exception->what(); + // L is the lua state, which you can wrap in a state_view if necessary + // maybe_exception will contain exception, if it exists + // description will either be the what() of the exception or a description saying that we hit the general-case catch(...) + if (maybe_exception) + { + const std::exception& ex = *maybe_exception; + LOG(FATAL) << ex.what(); + } else - LOG(WARNING) << what; + { + LOG(FATAL) << description; + } + Logger::FlushQueue(); - lua_pushlstring(L, what.data(), what.size()); - return 1; + // you must push 1 element onto the stack to be + // transported through as the error object in Lua + // note that Lua -- and 99.5% of all Lua users and libraries -- expects a string + // so we push a single string (in our case, the description of the error) + return sol::stack::push(L, description); } - inline int panic_handler(lua_State* L) + inline void panic_handler(sol::optional maybe_msg) { - size_t messagesize; - const char* message = lua_tolstring(L, -1, &messagesize); - if (message) + LOG(FATAL) << "Lua is in a panic state and will now abort() the application"; + if (maybe_msg) { - std::string err(message, messagesize); - lua_settop(L, 0); - LOG(FATAL) << err; + const std::string& msg = maybe_msg.value(); + LOG(FATAL) << "error message: " << msg; } - lua_settop(L, 0); - LOG(FATAL) << "An unexpected error occurred and panic has been invoked"; - return 1; + Logger::FlushQueue(); + + // When this function exits, Lua will exhibit default behavior and abort() } lua_module::lua_module(std::string module_name) : @@ -71,20 +82,18 @@ namespace big state["!module_name"] = module_name; state["!this"] = this; - state.set_exception_handler((sol::exception_handler_function)exception_handler); - state.set_panic(panic_handler); + state.set_exception_handler(exception_handler); + state.set_panic(sol::c_call); const auto script_file_path = g_lua_manager->get_scripts_folder().get_file(module_name).get_path(); m_last_write_time = std::filesystem::last_write_time(script_file_path); - auto result = state.load_file(script_file_path.string()); + + auto result = state.safe_script_file(script_file_path.string(), &sol::script_pass_on_error, sol::load_mode::text); if (!result.valid()) { - LOG(WARNING) << module_name << " failed to load: " << result.get().what(); - } - else - { - result(); + LOG(FATAL) << module_name << " failed to load: " << result.get().what(); + Logger::FlushQueue(); } } @@ -157,6 +166,7 @@ namespace big const auto module = sol::state_view(current_state)["!this"].get(); LOG(FATAL) << module->module_name() << " tried calling a currently not supported lua function: " << function_name; + Logger::FlushQueue(); }; } diff --git a/src/lua/lua_module.hpp b/src/lua/lua_module.hpp index cf3a94d9..458c4fe3 100644 --- a/src/lua/lua_module.hpp +++ b/src/lua/lua_module.hpp @@ -27,7 +27,7 @@ namespace big std::unordered_map> m_tab_to_sub_tabs; std::unordered_map>> m_gui; - std::unordered_map> m_event_callbacks; + std::unordered_map> m_event_callbacks; std::vector m_allocated_memory; lua_module(std::string module_name);