fix(lua): better exception handling (#1734)

This commit is contained in:
Quentin 2023-07-16 23:32:34 +02:00 committed by GitHub
parent 8d01f497e7
commit 0417fbf0f9
8 changed files with 60 additions and 51 deletions

View File

@ -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<big::lua_module*>();

View File

@ -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<lua::gui::button> add_button(const std::string& name, sol::function callback, sol::this_state state)
std::shared_ptr<lua::gui::button> add_button(const std::string& name, sol::protected_function callback, sol::this_state state)
{
auto element = std::make_shared<lua::gui::button>(name, callback);
add_element(state, m_tab_hash, element);

View File

@ -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)
{

View File

@ -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;
};

View File

@ -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<big::lua_module*>();
std::unique_ptr<big::script> lua_script = std::make_unique<big::script>(
[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));
@ -117,23 +115,23 @@ namespace lua::script
// 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<big::lua_module*>();

View File

@ -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<std::string_view>() << ": " << error.what();
LOG(FATAL) << state["!module_name"].get<std::string_view>() << ": " << error.what();
Logger::FlushQueue();
}
void lua_manager::load_all_modules()

View File

@ -18,30 +18,41 @@
namespace big
{
inline int exception_handler(lua_State* L, sol::optional<const std::exception&> exception, std::string_view what)
// https://sol2.readthedocs.io/en/latest/exceptions.html
int exception_handler(lua_State* L, sol::optional<const std::exception&> 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<std::string> 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<decltype(&panic_handler), &panic_handler>);
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<sol::error>().what();
}
else
{
result();
LOG(FATAL) << module_name << " failed to load: " << result.get<sol::error>().what();
Logger::FlushQueue();
}
}
@ -157,6 +166,7 @@ namespace big
const auto module = sol::state_view(current_state)["!this"].get<big::lua_module*>();
LOG(FATAL) << module->module_name() << " tried calling a currently not supported lua function: " << function_name;
Logger::FlushQueue();
};
}

View File

@ -27,7 +27,7 @@ namespace big
std::unordered_map<big::tabs, std::vector<big::tabs>> m_tab_to_sub_tabs;
std::unordered_map<rage::joaat_t, std::vector<std::shared_ptr<lua::gui::gui_element>>> m_gui;
std::unordered_map<menu_event, std::vector<sol::function>> m_event_callbacks;
std::unordered_map<menu_event, std::vector<sol::protected_function>> m_event_callbacks;
std::vector<void*> m_allocated_memory;
lua_module(std::string module_name);