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
15 changed files with 288 additions and 300 deletions

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