lua: sub folders support, check for .lua file extension, refactor and simplify internals. (#1775)

This commit is contained in:
Quentin 2023-07-22 13:05:43 +02:00 committed by GitHub
parent 093abc72da
commit fa8043be2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 288 additions and 300 deletions

View File

@ -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*>();
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>("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<sol::table>();

View File

@ -4,19 +4,16 @@
namespace lua::gui
{
static void add_independent_element(lua_State* state, std::shared_ptr<lua::gui::gui_element> element)
static void add_independent_element(lua_State* state, std::unique_ptr<lua::gui::gui_element> element)
{
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
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<lua::gui::gui_element> element)
static void add_element(lua_State* state, uint32_t hash, std::unique_ptr<lua::gui::gui_element> element)
{
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
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*>();
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*>();
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*>();
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*>();
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<lua::gui::button> 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<lua::gui::button>(name, callback);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::button>(name, callback);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::text> 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<lua::gui::text>(name);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::text>(name);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::checkbox> 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<lua::gui::checkbox>(name);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::checkbox>(name);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::sameline> tab::add_sameline(sol::this_state state)
lua::gui::sameline* tab::add_sameline(sol::this_state state)
{
auto element = std::make_shared<lua::gui::sameline>();
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::sameline>();
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::separator> tab::add_separator(sol::this_state state)
lua::gui::separator* tab::add_separator(sol::this_state state)
{
auto element = std::make_shared<lua::gui::separator>();
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::separator>();
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::input_int> 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<lua::gui::input_int>(name);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::input_int>(name);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::input_float> 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<lua::gui::input_float>(name);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::input_float>(name);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::input_string> 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<lua::gui::input_string>(name);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::input_string>(name);
auto el_ptr = element.get();
add_element(state, m_tab_hash, std::move(element));
return el_ptr;
}
std::shared_ptr<lua::gui::raw_imgui_callback> 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<lua::gui::raw_imgui_callback>(imgui_rendering);
add_element(state, m_tab_hash, element);
return element;
auto element = std::make_unique<lua::gui::raw_imgui_callback>(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<lua::gui::raw_imgui_callback> 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<lua::gui::raw_imgui_callback>(imgui_rendering);
add_independent_element(state, element);
return element;
auto element = std::make_unique<lua::gui::raw_imgui_callback>(imgui_rendering);
auto el_ptr = element.get();
add_independent_element(state, std::move(element));
return el_ptr;
}
void bind(sol::state& state)

View File

@ -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<lua::gui::button> 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<lua::gui::text> 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<lua::gui::checkbox> 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<lua::gui::sameline> 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<lua::gui::separator> 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<lua::gui::input_int> 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<lua::gui::input_float> 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<lua::gui::input_string> 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<lua::gui::raw_imgui_callback> 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);

View File

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

View File

@ -172,9 +172,10 @@ namespace lua::memory
template<typename T>
big::lua_patch* patch(T value, sol::this_state state)
{
auto module = sol::state_view(state)["!this"].get<big::lua_module*>();
auto patch = std::make_shared<big::lua_patch>(::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<big::lua_patch>(::memory::byte_patch::make((T*)m_address, value).get());
auto raw = patch.get();
module->m_registered_patches.push_back(std::move(patch));
return raw;
}

View File

@ -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*>();
big::lua_module* module = sol::state_view(state)["!this"];
std::unique_ptr<big::script> lua_script = std::make_unique<big::script>(
[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*>();
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)

View File

@ -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<lua_module>(module_name, m_scripts_folder));
const auto module = std::make_shared<lua_module>(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<void(std::weak_ptr<lua_module>)> 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_module> 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()
{

View File

@ -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<std::shared_ptr<lua_module>> m_modules;
struct module_load_info
{
std::string m_name;
std::function<void(std::weak_ptr<lua_module>)> m_on_module_loaded;
};
std::queue<module_load_info> 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<lua_module> 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<void(std::weak_ptr<lua_module>)> on_module_loaded);
void load_modules_from_queue();
std::weak_ptr<lua_module> get_module(rage::joaat_t module_id);
void handle_error(const sol::error& error, const sol::state_view& state);
template<menu_event menu_event_, typename Return = void, typename... Args>

View File

@ -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<sol::state>();
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<decltype(&panic_handler), &panic_handler>);
m_state.set_exception_handler(exception_handler);
m_state.set_panic(sol::c_call<decltype(&panic_handler), &panic_handler>);
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<sol::error>().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<sol::state> lua_state_shared = std::shared_ptr<sol::state>(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<std::chrono::file_clock> 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<size_t N>
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<big::lua_module*>();
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<sol::error>().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();
});
}
}

View File

@ -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 <services/gui/gui_service.hpp>
namespace big
{
class script;
class lua_module
{
std::unique_ptr<sol::state> 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<std::chrono::file_clock> m_last_write_time;
std::mutex m_registered_scripts_mutex;
public:
std::vector<script*> m_registered_scripts;
std::vector<std::shared_ptr<lua_patch>> m_registered_patches;
std::vector<std::unique_ptr<script>> m_registered_scripts;
std::vector<std::unique_ptr<lua_patch>> m_registered_patches;
std::vector<big::tabs> m_owned_tabs;
std::unordered_map<big::tabs, std::vector<big::tabs>> m_tab_to_sub_tabs;
std::vector<std::shared_ptr<lua::gui::gui_element>> m_independent_gui;
std::unordered_map<rage::joaat_t, std::vector<std::shared_ptr<lua::gui::gui_element>>> m_gui;
std::vector<std::unique_ptr<lua::gui::gui_element>> m_independent_gui;
std::unordered_map<rage::joaat_t, std::vector<std::unique_ptr<lua::gui::gui_element>>> m_gui;
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, 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<std::chrono::file_clock> 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();
};
}

View File

@ -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();

View File

@ -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<void(void)>;
bool m_should_be_deleted;
public:
explicit script(const func_t func, const std::string& name, const bool toggleable = true, const std::optional<std::size_t> stack_size = std::nullopt);
explicit script(const func_t func, const std::optional<std::size_t> 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<std::chrono::high_resolution_clock::duration> time = std::nullopt);
static script* get_current();

View File

@ -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<script> script)
void script_mgr::add_script(std::unique_ptr<script> script)
{
std::lock_guard lock(m_mutex);
auto* ret = script.get();
m_scripts_to_add.push_back(std::move(script));
return ret;
}
void script_mgr::remove_script(script* scr)
{
std::lock_guard lock(m_mutex);
scr->m_should_be_deleted = true;
m_scripts.push_back(std::move(script));
}
void script_mgr::remove_all_scripts()
@ -33,11 +23,6 @@ namespace big
m_scripts.clear();
}
void script_mgr::add_on_script_batch_removed(std::function<void()> f)
{
m_on_script_batch_removed.push(f);
}
void script_mgr::tick()
{
gta_util::execute_as_script(RAGE_JOAAT("main_persistent"), std::mem_fn(&script_mgr::tick_internal), this);
@ -50,54 +35,23 @@ namespace big
m_can_tick = true;
}
static void lua_manager_tick()
{
g_lua_manager->reload_changed_scripts();
g_lua_manager->for_each_module([](const std::shared_ptr<lua_module>& module) {
module->tick_scripts();
module->cleanup_done_scripts();
});
}
void script_mgr::tick_internal()
{
static bool ensure_it = (ensure_main_fiber(), true);
std::lock_guard lock(m_mutex);
if (g_lua_manager->m_schedule_reload_modules)
{
g_lua_manager->unload_all_modules();
}
g_lua_manager->reload_changed_scripts();
std::erase_if(m_scripts, [](std::unique_ptr<script>& iter) {
return iter->m_should_be_deleted;
});
while (m_on_script_batch_removed.size())
{
auto& f = m_on_script_batch_removed.front();
if (f)
{
f();
}
m_on_script_batch_removed.pop();
}
if (g_lua_manager->m_schedule_reload_modules)
{
g_lua_manager->load_all_modules();
g_lua_manager->m_schedule_reload_modules = false;
}
else
{
g_lua_manager->load_modules_from_queue();
}
if (m_scripts_to_add.size())
{
for (auto& script_to_add : m_scripts_to_add)
{
m_scripts.push_back(std::move(script_to_add));
}
m_scripts_to_add.clear();
}
lua_manager_tick();
for (const auto& script : m_scripts)
{

View File

@ -1,6 +1,7 @@
#pragma once
#include "common.hpp"
#include "script.hpp"
#include "lua/lua_manager.hpp"
namespace big
{
@ -12,12 +13,9 @@ namespace big
explicit script_mgr() = default;
~script_mgr() = default;
script* add_script(std::unique_ptr<script> script);
void remove_script(script* script);
void add_script(std::unique_ptr<script> script);
void remove_all_scripts();
void add_on_script_batch_removed(std::function<void()> f);
inline void for_each_script(auto func)
{
std::lock_guard lock(m_mutex);
@ -26,6 +24,12 @@ namespace big
{
func(script);
}
g_lua_manager->for_each_module([&func](const std::shared_ptr<lua_module>& module) {
module->for_each_script([&func](script* script) {
func(script);
});
});
}
void tick();
@ -42,8 +46,6 @@ namespace big
private:
std::recursive_mutex m_mutex;
script_list m_scripts;
script_list m_scripts_to_add;
std::queue<std::function<void()>> m_on_script_batch_removed;
bool m_can_tick = false;
};

View File

@ -18,8 +18,8 @@ namespace big
{
g_lua_manager->for_each_module([](auto& module) {
if (ImGui::Selectable(module->module_name().c_str(),
!selected_module.expired() && selected_module.lock().get() == module.get()))
selected_module = module;
!selected_module.expired() && selected_module.lock().get() == module.get()))
selected_module = module;
});
ImGui::EndListBox();
@ -37,13 +37,12 @@ namespace big
if (components::button("Reload"))
{
auto name = selected_module.lock()->module_name();
auto id = selected_module.lock()->module_id();
const std::filesystem::path module_path = selected_module.lock()->module_path();
const auto id = selected_module.lock()->module_id();
g_lua_manager->unload_module(id);
g_lua_manager->queue_load_module(name, [](std::weak_ptr<big::lua_module> loaded_module) {
selected_module = loaded_module;
});
g_lua_manager->load_module(module_path);
selected_module = g_lua_manager->get_module(id);
}
}
@ -51,7 +50,8 @@ namespace big
if (components::button("Reload All"))
{
g_lua_manager->m_schedule_reload_modules = true;
g_lua_manager->unload_all_modules();
g_lua_manager->load_all_modules();
}
ImGui::SameLine();
ImGui::Checkbox("Auto Reload Changed Scripts", &g.lua.enable_auto_reload_changed_scripts);