From fa8043be2a71a07f042b1091d0f04c6751e059bd Mon Sep 17 00:00:00 2001 From: Quentin Date: Sat, 22 Jul 2023 13:05:43 +0200 Subject: [PATCH] lua: sub folders support, check for .lua file extension, refactor and simplify internals. (#1775) --- src/lua/bindings/event.cpp | 21 +-- src/lua/bindings/gui.cpp | 115 ++++++++-------- src/lua/bindings/gui.hpp | 18 +-- src/lua/bindings/memory.cpp | 10 +- src/lua/bindings/memory.hpp | 7 +- src/lua/bindings/script.cpp | 13 +- src/lua/lua_manager.cpp | 56 +++----- src/lua/lua_manager.hpp | 24 +--- src/lua/lua_module.cpp | 170 ++++++++++++++---------- src/lua/lua_module.hpp | 38 ++++-- src/script.cpp | 9 +- src/script.hpp | 5 +- src/script_mgr.cpp | 72 ++-------- src/script_mgr.hpp | 14 +- src/views/settings/view_lua_scripts.cpp | 16 +-- 15 files changed, 288 insertions(+), 300 deletions(-) diff --git a/src/lua/bindings/event.cpp b/src/lua/bindings/event.cpp index fd7c1b7f..cb31d497 100644 --- a/src/lua/bindings/event.cpp +++ b/src/lua/bindings/event.cpp @@ -1,5 +1,6 @@ #pragma once #include "event.hpp" + #include "fiber_pool.hpp" #include "lua/lua_module.hpp" #include "script_mgr.hpp" @@ -91,22 +92,22 @@ namespace lua::event // 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::protected_function func, sol::this_state state) { - const auto module = sol::state_view(state)["!this"].get(); - + big::lua_module* module = sol::state_view(state)["!this"]; + module->m_event_callbacks[menu_event].push_back(func); } void bind(sol::state& state) { state.new_enum("menu_event", - { - {"PlayerLeave", menu_event::PlayerLeave}, - {"PlayerJoin", menu_event::PlayerJoin}, - {"PlayerMgrInit", menu_event::PlayerMgrInit}, - {"PlayerMgrShutdown", menu_event::PlayerMgrShutdown}, - {"ChatMessageReceived", menu_event::ChatMessageReceived}, - {"ScriptedGameEventReceived", menu_event::ScriptedGameEventReceived}, - }); + { + {"PlayerLeave", menu_event::PlayerLeave}, + {"PlayerJoin", menu_event::PlayerJoin}, + {"PlayerMgrInit", menu_event::PlayerMgrInit}, + {"PlayerMgrShutdown", menu_event::PlayerMgrShutdown}, + {"ChatMessageReceived", menu_event::ChatMessageReceived}, + {"ScriptedGameEventReceived", menu_event::ScriptedGameEventReceived}, + }); auto ns = state["event"].get_or_create(); diff --git a/src/lua/bindings/gui.cpp b/src/lua/bindings/gui.cpp index 9128902f..a55b2a4e 100644 --- a/src/lua/bindings/gui.cpp +++ b/src/lua/bindings/gui.cpp @@ -4,19 +4,16 @@ namespace lua::gui { - static void add_independent_element(lua_State* state, std::shared_ptr element) + static void add_independent_element(lua_State* state, std::unique_ptr element) { - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; module->m_independent_gui.push_back(std::move(element)); } - static void add_element(lua_State* state, uint32_t hash, std::shared_ptr element) + static void add_element(lua_State* state, uint32_t hash, std::unique_ptr element) { - auto module = sol::state_view(state)["!this"].get(); - - if (!module->m_gui.contains(hash)) - module->m_gui[hash] = {}; + big::lua_module* module = sol::state_view(state)["!this"]; module->m_gui[hash].push_back(std::move(element)); } @@ -56,9 +53,12 @@ namespace lua::gui { if (nav_item.second.hash == existing_tab_hash) { - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; + + module->m_tab_to_sub_tabs[nav_item.first].push_back(new_tab.first); + nav_item.second.sub_nav.emplace(new_tab); return; } @@ -94,7 +94,7 @@ namespace lua::gui // add top tab nav.emplace(make_tab_nav(name, m_tab_hash, state)); - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; module->m_owned_tabs.push_back(id()); } @@ -111,7 +111,7 @@ namespace lua::gui const auto sub_tab = make_tab_nav(name, m_tab_hash, state); add_to_existing_tab(nav, parent_tab_hash, sub_tab, state); - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; module->m_owned_tabs.push_back(id()); } @@ -122,10 +122,9 @@ namespace lua::gui void tab::clear(sol::this_state state) { - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; - if (module->m_gui.contains(m_tab_hash)) - module->m_gui[m_tab_hash] = {}; + module->m_gui[m_tab_hash].clear(); for (auto sub_tab : module->m_tab_to_sub_tabs[id()]) { @@ -146,67 +145,76 @@ namespace lua::gui return sub_tab; } - std::shared_ptr tab::add_button(const std::string& name, sol::protected_function callback, sol::this_state state) + lua::gui::button* tab::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); - return element; + auto element = std::make_unique(name, callback); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_text(const std::string& name, sol::this_state state) + lua::gui::text* tab::add_text(const std::string& name, sol::this_state state) { - auto element = std::make_shared(name); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(name); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_checkbox(const std::string& name, sol::this_state state) + lua::gui::checkbox* tab::add_checkbox(const std::string& name, sol::this_state state) { - auto element = std::make_shared(name); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(name); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_sameline(sol::this_state state) + lua::gui::sameline* tab::add_sameline(sol::this_state state) { - auto element = std::make_shared(); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_separator(sol::this_state state) + lua::gui::separator* tab::add_separator(sol::this_state state) { - auto element = std::make_shared(); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_input_int(const std::string& name, sol::this_state state) + lua::gui::input_int* tab::add_input_int(const std::string& name, sol::this_state state) { - auto element = std::make_shared(name); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(name); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_input_float(const std::string& name, sol::this_state state) + lua::gui::input_float* tab::add_input_float(const std::string& name, sol::this_state state) { - auto element = std::make_shared(name); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(name); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_input_string(const std::string& name, sol::this_state state) + lua::gui::input_string* tab::add_input_string(const std::string& name, sol::this_state state) { - auto element = std::make_shared(name); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(name); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } - std::shared_ptr tab::add_imgui(sol::protected_function imgui_rendering, sol::this_state state) + lua::gui::raw_imgui_callback* tab::add_imgui(sol::protected_function imgui_rendering, sol::this_state state) { - auto element = std::make_shared(imgui_rendering); - add_element(state, m_tab_hash, element); - return element; + auto element = std::make_unique(imgui_rendering); + auto el_ptr = element.get(); + add_element(state, m_tab_hash, std::move(element)); + return el_ptr; } // Lua API: Table @@ -296,11 +304,12 @@ namespace lua::gui // end // end) // ``` - static std::shared_ptr add_imgui(sol::protected_function imgui_rendering, sol::this_state state) + static lua::gui::raw_imgui_callback* add_imgui(sol::protected_function imgui_rendering, sol::this_state state) { - auto element = std::make_shared(imgui_rendering); - add_independent_element(state, element); - return element; + auto element = std::make_unique(imgui_rendering); + auto el_ptr = element.get(); + add_independent_element(state, std::move(element)); + return el_ptr; } void bind(sol::state& state) diff --git a/src/lua/bindings/gui.hpp b/src/lua/bindings/gui.hpp index 805c14ba..d92927d6 100644 --- a/src/lua/bindings/gui.hpp +++ b/src/lua/bindings/gui.hpp @@ -59,7 +59,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::protected_function callback, sol::this_state state); + lua::gui::button* add_button(const std::string& name, sol::protected_function callback, sol::this_state state); // Lua API: Function // Class: tab @@ -67,7 +67,7 @@ namespace lua::gui // Param: name: string: Text that will be written. // Returns: text: The text object instance. // Add text to the gui tab. - std::shared_ptr add_text(const std::string& name, sol::this_state state); + lua::gui::text* add_text(const std::string& name, sol::this_state state); // Lua API: Function // Class: tab @@ -75,21 +75,21 @@ namespace lua::gui // Param: name: string: Text that will be written next to the checkbox. // Returns: checkbox: The checkbox object instance. // Add a checkbox widget to the gui tab. - std::shared_ptr add_checkbox(const std::string& name, sol::this_state state); + lua::gui::checkbox* add_checkbox(const std::string& name, sol::this_state state); // Lua API: Function // Class: tab // Name: add_sameline // Returns: sameline: The sameline object instance. // Add a ImGui::SameLine. - std::shared_ptr add_sameline(sol::this_state state); + lua::gui::sameline* add_sameline(sol::this_state state); // Lua API: Function // Class: tab // Name: add_separator // Returns: separator: The separator object instance. // Add a ImGui::Separator. - std::shared_ptr add_separator(sol::this_state state); + lua::gui::separator* add_separator(sol::this_state state); // Lua API: Function // Class: tab @@ -97,7 +97,7 @@ namespace lua::gui // Param: name: string: Text that will be written next to the input field. // Returns: input_int: The input_int object instance. // Add a ImGui::InputInt. - std::shared_ptr add_input_int(const std::string& name, sol::this_state state); + lua::gui::input_int* add_input_int(const std::string& name, sol::this_state state); // Lua API: Function // Class: tab @@ -105,7 +105,7 @@ namespace lua::gui // Param: name: string: Text that will be written next to the input field. // Returns: input_float: The input_float object instance. // Add a ImGui::InputFloat. - std::shared_ptr add_input_float(const std::string& name, sol::this_state state); + lua::gui::input_float* add_input_float(const std::string& name, sol::this_state state); // Lua API: Function // Class: tab @@ -113,7 +113,7 @@ namespace lua::gui // Param: name: string: Text that will be written next to the input field. // Returns: input_string: The input_string object instance. // Add a ImGui::InputText. - std::shared_ptr add_input_string(const std::string& name, sol::this_state state); + lua::gui::input_string* add_input_string(const std::string& name, sol::this_state state); // Lua API: Function // Class: tab @@ -134,7 +134,7 @@ namespace lua::gui // end // end) // ``` - std::shared_ptr add_imgui(sol::protected_function imgui_rendering, sol::this_state state); + lua::gui::raw_imgui_callback* add_imgui(sol::protected_function imgui_rendering, sol::this_state state); }; void bind(sol::state& state); diff --git a/src/lua/bindings/memory.cpp b/src/lua/bindings/memory.cpp index 224754d0..ce0d07c8 100644 --- a/src/lua/bindings/memory.cpp +++ b/src/lua/bindings/memory.cpp @@ -107,9 +107,11 @@ namespace lua::memory // Returns: pointer: A pointer to the newly allocated memory. static pointer allocate(int size, sol::this_state state) { - void* mem = new uint8_t[](size); - auto module = sol::state_view(state)["!this"].get(); + void* mem = new uint8_t[](size); + + big::lua_module* module = sol::state_view(state)["!this"]; module->m_allocated_memory.push_back(mem); + return pointer((uint64_t)mem); } @@ -120,7 +122,9 @@ namespace lua::memory static void free(pointer ptr, sol::this_state state) { delete[] (void*)ptr.get_address(); - auto module = sol::state_view(state)["!this"].get(); + + big::lua_module* module = sol::state_view(state)["!this"]; + std::erase_if(module->m_allocated_memory, [ptr](void* addr) { return ptr.get_address() == (uint64_t)addr; }); diff --git a/src/lua/bindings/memory.hpp b/src/lua/bindings/memory.hpp index e2840746..8f86fadc 100644 --- a/src/lua/bindings/memory.hpp +++ b/src/lua/bindings/memory.hpp @@ -172,9 +172,10 @@ namespace lua::memory template big::lua_patch* patch(T value, sol::this_state state) { - auto module = sol::state_view(state)["!this"].get(); - auto patch = std::make_shared(::memory::byte_patch::make((T*)m_address, value).get()); - auto raw = patch.get(); + big::lua_module* module = sol::state_view(state)["!this"]; + + auto patch = std::make_unique(::memory::byte_patch::make((T*)m_address, value).get()); + auto raw = patch.get(); module->m_registered_patches.push_back(std::move(patch)); return raw; } diff --git a/src/lua/bindings/script.cpp b/src/lua/bindings/script.cpp index 72b4d625..13996956 100644 --- a/src/lua/bindings/script.cpp +++ b/src/lua/bindings/script.cpp @@ -53,7 +53,7 @@ namespace lua::script // ``` 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* module = sol::state_view(state)["!this"]; std::unique_ptr lua_script = std::make_unique( [func_, state]() mutable { @@ -79,9 +79,7 @@ namespace lua::script }, name); - const auto registered_script = big::g_script_mgr.add_script(std::move(lua_script)); - - module->m_registered_scripts.push_back(registered_script); + module->m_registered_scripts.push_back(std::move(lua_script)); } // Lua API: Function @@ -114,7 +112,7 @@ namespace lua::script // ``` static void run_in_fiber(sol::protected_function func_, sol::this_state state) { - auto module = sol::state_view(state)["!this"].get(); + big::lua_module* module = sol::state_view(state)["!this"]; static size_t name_i = 0; std::string job_name = module->module_name() + std::to_string(name_i++); @@ -138,16 +136,13 @@ namespace lua::script } else { - big::g_script_mgr.remove_script(big::script::get_current()); break; } } }, job_name); - const auto registered_script = big::g_script_mgr.add_script(std::move(lua_script)); - - module->m_registered_scripts.push_back(registered_script); + module->m_registered_scripts.push_back(std::move(lua_script)); } void bind(sol::state& state) diff --git a/src/lua/lua_manager.cpp b/src/lua/lua_manager.cpp index f03d8b89..911245d4 100644 --- a/src/lua/lua_manager.cpp +++ b/src/lua/lua_manager.cpp @@ -7,10 +7,7 @@ namespace big lua_manager::lua_manager(folder scripts_folder) : m_scripts_folder(scripts_folder) { - m_schedule_reload_modules = false; - - m_wake_time_changed_scripts_check = - std::chrono::high_resolution_clock::now() + m_delay_between_changed_scripts_check; + m_wake_time_changed_scripts_check = std::chrono::high_resolution_clock::now() + m_delay_between_changed_scripts_check; g_lua_manager = this; @@ -89,17 +86,27 @@ namespace big }); } - void lua_manager::load_module(const std::string& module_name) + void lua_manager::load_module(const std::filesystem::path& module_path) { std::lock_guard guard(m_module_lock); + const auto module_name = module_path.filename().string(); + const auto id = rage::joaat(module_name); for (const auto& module : m_modules) + { if (module->module_id() == id) + { + LOG(WARNING) << "Module with the name " << module_name << " already loaded."; return; + } + } - m_modules.push_back(std::make_shared(module_name, m_scripts_folder)); + const auto module = std::make_shared(module_path, m_scripts_folder); + module->load_and_call_script(); + + m_modules.push_back(module); } void lua_manager::reload_changed_scripts() @@ -111,20 +118,19 @@ namespace big if (m_wake_time_changed_scripts_check <= std::chrono::high_resolution_clock::now()) { - for (const auto& entry : std::filesystem::directory_iterator(m_scripts_folder.get_path())) + for (const auto& entry : std::filesystem::recursive_directory_iterator(m_scripts_folder.get_path(), std::filesystem::directory_options::skip_permission_denied)) { if (entry.is_regular_file()) { - const auto module_name = entry.path().filename().string(); + const auto& module_path = entry.path(); const auto last_write_time = entry.last_write_time(); for (const auto& module : m_modules) { - if (module->module_name() == module_name && - module->last_write_time() < last_write_time) + if (module->module_path() == module_path && module->last_write_time() < last_write_time) { unload_module(module->module_id()); - queue_load_module(module_name, nullptr); + load_module(module_path); break; } } @@ -135,28 +141,6 @@ namespace big } } - void lua_manager::queue_load_module(const std::string& module_name, std::function)> on_module_loaded) - { - m_modules_load_queue.push({module_name, on_module_loaded}); - } - - void lua_manager::load_modules_from_queue() - { - while (m_modules_load_queue.size()) - { - auto& module_load_info = m_modules_load_queue.front(); - - const auto id = rage::joaat(module_load_info.m_name); - - load_module(module_load_info.m_name); - auto loaded_module = get_module(id); - if (module_load_info.m_on_module_loaded) - module_load_info.m_on_module_loaded(loaded_module); - - m_modules_load_queue.pop(); - } - } - std::weak_ptr lua_manager::get_module(rage::joaat_t module_id) { std::lock_guard guard(m_module_lock); @@ -176,9 +160,9 @@ namespace big void lua_manager::load_all_modules() { - for (const auto& entry : std::filesystem::directory_iterator(m_scripts_folder.get_path())) - if (entry.is_regular_file()) - load_module(entry.path().filename().string()); + for (const auto& entry : std::filesystem::recursive_directory_iterator(m_scripts_folder.get_path(), std::filesystem::directory_options::skip_permission_denied)) + if (entry.is_regular_file() && entry.path().extension() == ".lua") + load_module(entry.path()); } void lua_manager::unload_all_modules() { diff --git a/src/lua/lua_manager.hpp b/src/lua/lua_manager.hpp index 340cbdd9..f953b721 100644 --- a/src/lua/lua_manager.hpp +++ b/src/lua/lua_manager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "lua_module.hpp" #include "core/enums.hpp" +#include "lua_module.hpp" namespace big { @@ -10,21 +10,11 @@ namespace big std::mutex m_module_lock; std::vector> m_modules; - struct module_load_info - { - std::string m_name; - std::function)> m_on_module_loaded; - }; - std::queue m_modules_load_queue; - static constexpr std::chrono::seconds m_delay_between_changed_scripts_check = 3s; std::chrono::high_resolution_clock::time_point m_wake_time_changed_scripts_check; folder m_scripts_folder; - public: - bool m_schedule_reload_modules; - public: lua_manager(folder scripts_folder); ~lua_manager(); @@ -37,28 +27,22 @@ namespace big return m_modules.size(); } - - bool has_gui_to_draw(rage::joaat_t tab_hash); - inline const folder& get_scripts_folder() const { return m_scripts_folder; } + std::weak_ptr get_module(rage::joaat_t module_id); + bool has_gui_to_draw(rage::joaat_t tab_hash); void draw_independent_gui(); void draw_gui(rage::joaat_t tab_hash); void unload_module(rage::joaat_t module_id); - void load_module(const std::string& module_name); + void load_module(const std::filesystem::path& module_path); void reload_changed_scripts(); - void queue_load_module(const std::string& module_name, std::function)> on_module_loaded); - void load_modules_from_queue(); - - std::weak_ptr get_module(rage::joaat_t module_id); - void handle_error(const sol::error& error, const sol::state_view& state); template diff --git a/src/lua/lua_module.cpp b/src/lua/lua_module.cpp index df3b3577..c61df5fd 100644 --- a/src/lua/lua_module.cpp +++ b/src/lua/lua_module.cpp @@ -57,16 +57,14 @@ namespace big // When this function exits, Lua will exhibit default behavior and abort() } - lua_module::lua_module(std::string module_name, folder& scripts_folder) : - m_module_name(module_name), - m_module_id(rage::joaat(module_name)) + lua_module::lua_module(const std::filesystem::path& module_path, folder& scripts_folder) : + m_state(), + m_module_path(module_path), + m_module_name(module_path.filename().string()), + m_module_id(rage::joaat(m_module_name)) { - m_state = std::make_unique(); - - auto& state = *m_state; - // clang-format off - state.open_libraries( + m_state.open_libraries( sol::lib::base, sol::lib::package, sol::lib::coroutine, @@ -81,44 +79,27 @@ namespace big init_lua_api(scripts_folder); - state["!module_name"] = module_name; - state["!this"] = this; + m_state["!module_name"] = m_module_name; + m_state["!this"] = this; - state.set_exception_handler(exception_handler); - state.set_panic(sol::c_call); + m_state.set_exception_handler(exception_handler); + m_state.set_panic(sol::c_call); - const auto script_file_path = scripts_folder.get_file(module_name).get_path(); - m_last_write_time = std::filesystem::last_write_time(script_file_path); - - auto result = state.safe_script_file(script_file_path.string(), &sol::script_pass_on_error, sol::load_mode::text); - - if (!result.valid()) - { - LOG(FATAL) << module_name << " failed to load: " << result.get().what(); - Logger::FlushQueue(); - } + m_last_write_time = std::filesystem::last_write_time(m_module_path); } lua_module::~lua_module() { + { + std::lock_guard guard(m_registered_scripts_mutex); + m_registered_scripts.clear(); + } + for (const auto owned_tab : m_owned_tabs) { big::g_gui_service->remove_from_nav(owned_tab); } - for (auto script : m_registered_scripts) - { - g_script_mgr.remove_script(script); - } - - // the lua state is about to be destroyed, - // but we need to keep it around a little bit longer - // until the script manager properly finish executing any potential lua script. - // There are most likely much better ways of doing all this, feel free to refactor I guess. - std::shared_ptr lua_state_shared = std::shared_ptr(std::move(m_state)); - g_script_mgr.add_on_script_batch_removed([lua_state_shared] { - }); - for (auto memory : m_allocated_memory) delete[] memory; } @@ -133,6 +114,11 @@ namespace big return m_module_name; } + const std::filesystem::path& lua_module::module_path() const + { + return m_module_path; + } + const std::chrono::time_point lua_module::last_write_time() const { return m_last_write_time; @@ -140,32 +126,39 @@ namespace big void lua_module::set_folder_for_lua_require(folder& scripts_folder) { - auto& state = *m_state; + std::string scripts_search_path = scripts_folder.get_path().string() + "/?.lua;"; - const auto scripts_search_path = scripts_folder.get_path() / "?.lua"; - state["package"]["path"] = scripts_search_path.string(); + for (const auto& entry : std::filesystem::recursive_directory_iterator(scripts_folder.get_path(), std::filesystem::directory_options::skip_permission_denied)) + { + if (!entry.is_directory()) + continue; + + scripts_search_path += entry.path().string() + "/?.lua;"; + } + // Remove final ';' + scripts_search_path.pop_back(); + + m_state["package"]["path"] = scripts_search_path; } void lua_module::sandbox_lua_os_library() { - auto& state = *m_state; - - const auto& os = state["os"]; - sol::table sandbox_os(state, sol::create); + const auto& os = m_state["os"]; + sol::table sandbox_os(m_state, sol::create); sandbox_os["clock"] = os["clock"]; sandbox_os["date"] = os["date"]; sandbox_os["difftime"] = os["difftime"]; sandbox_os["time"] = os["time"]; - state["os"] = sandbox_os; + m_state["os"] = sandbox_os; } template static constexpr auto not_supported_lua_function(const char (&function_name)[N]) { - return [function_name](sol::this_state current_state, sol::variadic_args args) { - const auto module = sol::state_view(current_state)["!this"].get(); + return [function_name](sol::this_state state, sol::variadic_args args) { + big::lua_module* module = sol::state_view(state)["!this"]; LOG(FATAL) << module->module_name() << " tried calling a currently not supported lua function: " << function_name; Logger::FlushQueue(); @@ -174,50 +167,83 @@ namespace big void lua_module::sandbox_lua_loads(folder& scripts_folder) { - auto& state = *m_state; - // That's from lua base lib, luaB - state["load"] = not_supported_lua_function("load"); - state["loadstring"] = not_supported_lua_function("loadstring"); - state["loadfile"] = not_supported_lua_function("loadfile"); - state["dofile"] = not_supported_lua_function("dofile"); + m_state["load"] = not_supported_lua_function("load"); + m_state["loadstring"] = not_supported_lua_function("loadstring"); + m_state["loadfile"] = not_supported_lua_function("loadfile"); + m_state["dofile"] = not_supported_lua_function("dofile"); // That's from lua package lib. // We only allow dependencies between .lua files, no DLLs. - state["package"]["loadlib"] = not_supported_lua_function("package.loadlib"); - state["package"]["cpath"] = ""; + m_state["package"]["loadlib"] = not_supported_lua_function("package.loadlib"); + m_state["package"]["cpath"] = ""; // 1 2 3 4 // {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL}; - state["package"]["searchers"][3] = not_supported_lua_function("package.searcher C"); - state["package"]["searchers"][4] = not_supported_lua_function("package.searcher Croot"); + m_state["package"]["searchers"][3] = not_supported_lua_function("package.searcher C"); + m_state["package"]["searchers"][4] = not_supported_lua_function("package.searcher Croot"); set_folder_for_lua_require(scripts_folder); } void lua_module::init_lua_api(folder& scripts_folder) { - auto& state = *m_state; - // https://blog.rubenwardy.com/2020/07/26/sol3-script-sandbox/ // https://www.lua.org/manual/5.4/manual.html#pdf-require sandbox_lua_os_library(); sandbox_lua_loads(scripts_folder); - lua::log::bind(state); - lua::globals::bind(state); - lua::script::bind(state); - lua::native::bind(state); - lua::memory::bind(state); - lua::gui::bind(state); - lua::network::bind(state); - lua::command::bind(state); - lua::tunables::bind(state); - lua::locals::bind(state); - lua::event::bind(state); - lua::vector::bind(state); - lua::global_table::bind(state); - lua::imgui::bind(state, state.globals()); - lua::entities::bind(state); + lua::log::bind(m_state); + lua::globals::bind(m_state); + lua::script::bind(m_state); + lua::native::bind(m_state); + lua::memory::bind(m_state); + lua::gui::bind(m_state); + lua::network::bind(m_state); + lua::command::bind(m_state); + lua::tunables::bind(m_state); + lua::locals::bind(m_state); + lua::event::bind(m_state); + lua::vector::bind(m_state); + lua::global_table::bind(m_state); + lua::imgui::bind(m_state, m_state.globals()); + lua::entities::bind(m_state); + } + + void lua_module::load_and_call_script() + { + auto result = m_state.safe_script_file(m_module_path.string(), &sol::script_pass_on_error, sol::load_mode::text); + + if (!result.valid()) + { + LOG(FATAL) << m_module_name << " failed to load: " << result.get().what(); + Logger::FlushQueue(); + } + else + { + LOG(INFO) << "Loaded " << m_module_name; + } + } + + void lua_module::tick_scripts() + { + std::lock_guard guard(m_registered_scripts_mutex); + + for (auto& script : m_registered_scripts) + { + if (script->is_enabled()) + { + script->tick(); + } + } + } + + void lua_module::cleanup_done_scripts() + { + std::lock_guard guard(m_registered_scripts_mutex); + + std::erase_if(m_registered_scripts, [](auto& script) { + return script->is_done(); + }); } } \ No newline at end of file diff --git a/src/lua/lua_module.hpp b/src/lua/lua_module.hpp index 8aa09cbf..fd0d21c3 100644 --- a/src/lua/lua_module.hpp +++ b/src/lua/lua_module.hpp @@ -1,39 +1,44 @@ #pragma once #include "bindings/gui/gui_element.hpp" +#include "core/data/menu_event.hpp" #include "lua_patch.hpp" #include "sol.hpp" -#include "core/data/menu_event.hpp" +#include "../script.hpp" #include namespace big { - class script; - class lua_module { - std::unique_ptr m_state; + sol::state m_state; + + std::filesystem::path m_module_path; std::string m_module_name; rage::joaat_t m_module_id; std::chrono::time_point m_last_write_time; + std::mutex m_registered_scripts_mutex; + public: - std::vector m_registered_scripts; - std::vector> m_registered_patches; + std::vector> m_registered_scripts; + std::vector> m_registered_patches; std::vector m_owned_tabs; std::unordered_map> m_tab_to_sub_tabs; - std::vector> m_independent_gui; - std::unordered_map>> m_gui; + std::vector> m_independent_gui; + std::unordered_map>> m_gui; std::unordered_map> m_event_callbacks; std::vector m_allocated_memory; - lua_module(std::string module_name, folder& scripts_folder); + lua_module(const std::filesystem::path& module_path, folder& scripts_folder); ~lua_module(); + const std::filesystem::path& module_path() const; + rage::joaat_t module_id() const; const std::string& module_name() const; const std::chrono::time_point last_write_time() const; @@ -45,5 +50,20 @@ namespace big void sandbox_lua_loads(folder& scripts_folder); void init_lua_api(folder& scripts_folder); + + void load_and_call_script(); + + inline void for_each_script(auto func) + { + std::lock_guard guard(m_registered_scripts_mutex); + + for (auto& script : m_registered_scripts) + { + func(script.get()); + } + } + + void tick_scripts(); + void cleanup_done_scripts(); }; } \ No newline at end of file diff --git a/src/script.cpp b/src/script.cpp index 8e9d0337..21f4b65f 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -18,7 +18,7 @@ namespace big m_script_fiber(nullptr), m_main_fiber(nullptr), m_func(func), - m_should_be_deleted(false) + m_done(false) { m_script_fiber = CreateFiber( stack_size.has_value() ? stack_size.value() : 0, @@ -61,6 +61,11 @@ namespace big return m_toggleable; } + bool script::is_done() const + { + return m_done; + } + void script::tick() { m_main_fiber = GetCurrentFiber(); @@ -93,6 +98,8 @@ namespace big { m_func(); + m_done = true; + while (true) { yield(); diff --git a/src/script.hpp b/src/script.hpp index 19cb6fb0..5c934a2a 100644 --- a/src/script.hpp +++ b/src/script.hpp @@ -8,12 +8,11 @@ namespace big std::string m_name; bool m_enabled; bool m_toggleable; + bool m_done; public: using func_t = std::function; - bool m_should_be_deleted; - public: explicit script(const func_t func, const std::string& name, const bool toggleable = true, const std::optional stack_size = std::nullopt); explicit script(const func_t func, const std::optional stack_size = std::nullopt); @@ -26,6 +25,8 @@ namespace big [[nodiscard]] bool is_toggleable() const; + [[nodiscard]] bool is_done() const; + void tick(); void yield(std::optional time = std::nullopt); static script* get_current(); diff --git a/src/script_mgr.cpp b/src/script_mgr.cpp index 57dae5d9..4b6e36c9 100644 --- a/src/script_mgr.cpp +++ b/src/script_mgr.cpp @@ -6,24 +6,14 @@ #include "gta_util.hpp" #include "invoker.hpp" #include "pointers.hpp" -#include "lua/lua_manager.hpp" namespace big { - script* script_mgr::add_script(std::unique_ptr