feat(LuaMgr): Disable and Enable scripts from UI and prevent crash when renaming the scripts/
dir. (#2493)
This commit is contained in:
@ -4,9 +4,29 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
std::optional<std::filesystem::path> move_file_relative_to_folder(const std::filesystem::path& original, const std::filesystem::path& target, const std::filesystem::path& file)
|
||||
{
|
||||
// keeps folder hierarchy intact
|
||||
const auto new_module_path = target / relative(file, original);
|
||||
g_file_manager.ensure_file_can_be_created(new_module_path);
|
||||
|
||||
try
|
||||
{
|
||||
rename(file, new_module_path);
|
||||
}
|
||||
catch(const std::filesystem::filesystem_error& e)
|
||||
{
|
||||
LOG(FATAL) << "Failed to move Lua file: " << e.what();
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
return { new_module_path };
|
||||
}
|
||||
|
||||
lua_manager::lua_manager(folder scripts_folder, folder scripts_config_folder) :
|
||||
m_scripts_folder(scripts_folder),
|
||||
m_scripts_config_folder(scripts_config_folder)
|
||||
m_scripts_config_folder(scripts_config_folder),
|
||||
m_disabled_scripts_folder(scripts_folder.get_folder("./disabled"))
|
||||
{
|
||||
m_wake_time_changed_scripts_check = std::chrono::high_resolution_clock::now() + m_delay_between_changed_scripts_check;
|
||||
|
||||
@ -22,6 +42,81 @@ namespace big
|
||||
g_lua_manager = nullptr;
|
||||
}
|
||||
|
||||
void lua_manager::disable_all_modules()
|
||||
{
|
||||
std::vector<std::filesystem::path> script_paths;
|
||||
|
||||
{
|
||||
std::lock_guard guard(m_module_lock);
|
||||
for (auto& module : m_modules)
|
||||
{
|
||||
script_paths.push_back(module->module_path());
|
||||
|
||||
module.reset();
|
||||
}
|
||||
m_modules.clear();
|
||||
}
|
||||
|
||||
for (const auto& script_path : script_paths)
|
||||
{
|
||||
const auto new_module_path = move_file_relative_to_folder(m_scripts_folder.get_path(), m_disabled_scripts_folder.get_path(), script_path);
|
||||
if (new_module_path)
|
||||
{
|
||||
load_module(*new_module_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lua_manager::enable_all_modules()
|
||||
{
|
||||
std::vector<std::filesystem::path> script_paths;
|
||||
|
||||
{
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
for (auto& module : m_disabled_modules)
|
||||
{
|
||||
script_paths.push_back(module->module_path());
|
||||
|
||||
module.reset();
|
||||
}
|
||||
m_disabled_modules.clear();
|
||||
}
|
||||
|
||||
for (const auto& script_path : script_paths)
|
||||
{
|
||||
const auto new_module_path = move_file_relative_to_folder(m_disabled_scripts_folder.get_path(), m_scripts_folder.get_path(), script_path);
|
||||
if (new_module_path)
|
||||
{
|
||||
load_module(*new_module_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lua_manager::load_all_modules()
|
||||
{
|
||||
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()
|
||||
{
|
||||
{
|
||||
std::lock_guard guard(m_module_lock);
|
||||
|
||||
for (auto& module : m_modules)
|
||||
module.reset();
|
||||
m_modules.clear();
|
||||
}
|
||||
{
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
|
||||
for (auto& module : m_disabled_modules)
|
||||
module.reset();
|
||||
m_disabled_modules.clear();
|
||||
}
|
||||
}
|
||||
|
||||
bool lua_manager::has_gui_to_draw(rage::joaat_t tab_hash)
|
||||
{
|
||||
std::lock_guard guard(m_module_lock);
|
||||
@ -78,6 +173,49 @@ namespace big
|
||||
}
|
||||
}
|
||||
|
||||
std::weak_ptr<lua_module> lua_manager::enable_module(rage::joaat_t module_id)
|
||||
{
|
||||
if (auto module = get_disabled_module(module_id).lock())
|
||||
{
|
||||
const auto module_path = module->module_path();
|
||||
|
||||
// unload module
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
std::erase_if(m_disabled_modules, [module_id](auto& module) {
|
||||
return module_id == module->module_id();
|
||||
});
|
||||
|
||||
const auto new_module_path = move_file_relative_to_folder(m_disabled_scripts_folder.get_path(), m_scripts_folder.get_path(), module_path);
|
||||
if (new_module_path)
|
||||
{
|
||||
return load_module(*new_module_path);
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::weak_ptr<lua_module> lua_manager::disable_module(rage::joaat_t module_id)
|
||||
{
|
||||
if (auto module = get_module(module_id).lock())
|
||||
{
|
||||
const auto module_path = module->module_path();
|
||||
|
||||
// unload module
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
std::erase_if(m_modules, [module_id](auto& module) {
|
||||
return module_id == module->module_id();
|
||||
});
|
||||
|
||||
const auto new_module_path = move_file_relative_to_folder(m_scripts_folder.get_path(), m_disabled_scripts_folder.get_path(), module_path);
|
||||
if (new_module_path)
|
||||
{
|
||||
return load_module(*new_module_path);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void lua_manager::unload_module(rage::joaat_t module_id)
|
||||
{
|
||||
std::lock_guard guard(m_module_lock);
|
||||
@ -87,33 +225,41 @@ namespace big
|
||||
});
|
||||
}
|
||||
|
||||
void lua_manager::load_module(const std::filesystem::path& module_path)
|
||||
std::weak_ptr<lua_module> lua_manager::load_module(const std::filesystem::path& module_path)
|
||||
{
|
||||
if (!std::filesystem::exists(module_path))
|
||||
{
|
||||
LOG(WARNING) << reinterpret_cast<const char*>(module_path.u8string().c_str()) << " does not exist in the filesystem. Not loading it.";
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::lock_guard guard(m_module_lock);
|
||||
|
||||
const auto module_name = module_path.filename().string();
|
||||
|
||||
const auto id = rage::joaat(module_name);
|
||||
|
||||
std::lock_guard guard(m_module_lock);
|
||||
for (const auto& module : m_modules)
|
||||
{
|
||||
if (module->module_id() == id)
|
||||
{
|
||||
LOG(WARNING) << "Module with the name " << module_name << " already loaded.";
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
const auto module = std::make_shared<lua_module>(module_path, m_scripts_folder);
|
||||
module->load_and_call_script();
|
||||
const auto rel = relative(module_path, m_disabled_scripts_folder.get_path());
|
||||
const auto is_disabled_module = !rel.empty() && rel.native()[0] != '.';
|
||||
const auto module = std::make_shared<lua_module>(module_path, m_scripts_folder, is_disabled_module);
|
||||
if (!module->is_disabled())
|
||||
{
|
||||
module->load_and_call_script();
|
||||
m_modules.push_back(module);
|
||||
|
||||
m_modules.push_back(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
std::lock_guard disabled_guard(m_disabled_module_lock);
|
||||
m_disabled_modules.push_back(module);
|
||||
return module;
|
||||
}
|
||||
|
||||
void lua_manager::reload_changed_scripts()
|
||||
@ -125,6 +271,12 @@ namespace big
|
||||
|
||||
if (m_wake_time_changed_scripts_check <= std::chrono::high_resolution_clock::now())
|
||||
{
|
||||
if (!exists(m_scripts_folder.get_path()))
|
||||
{
|
||||
// g_file_manager.ensure_folder_exists(m_scripts_folder.get_path());
|
||||
return;
|
||||
}
|
||||
|
||||
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())
|
||||
@ -159,25 +311,20 @@ namespace big
|
||||
return {};
|
||||
}
|
||||
|
||||
std::weak_ptr<lua_module> lua_manager::get_disabled_module(rage::joaat_t module_id)
|
||||
{
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
|
||||
for (const auto& module : m_disabled_modules)
|
||||
if (module->module_id() == module_id)
|
||||
return module;
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void lua_manager::handle_error(const sol::error& error, const sol::state_view& state)
|
||||
{
|
||||
LOG(FATAL) << state["!module_name"].get<std::string_view>() << ": " << error.what();
|
||||
Logger::FlushQueue();
|
||||
}
|
||||
|
||||
void lua_manager::load_all_modules()
|
||||
{
|
||||
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()
|
||||
{
|
||||
std::lock_guard guard(m_module_lock);
|
||||
|
||||
for (auto& module : m_modules)
|
||||
module.reset();
|
||||
|
||||
m_modules.clear();
|
||||
}
|
||||
}
|
@ -4,15 +4,18 @@
|
||||
|
||||
namespace big
|
||||
{
|
||||
class lua_manager
|
||||
class lua_manager final
|
||||
{
|
||||
private:
|
||||
std::mutex m_module_lock;
|
||||
std::vector<std::shared_ptr<lua_module>> m_modules;
|
||||
std::mutex m_disabled_module_lock;
|
||||
std::vector<std::shared_ptr<lua_module>> m_disabled_modules;
|
||||
|
||||
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_disabled_scripts_folder;
|
||||
folder m_scripts_folder;
|
||||
folder m_scripts_config_folder;
|
||||
|
||||
@ -20,6 +23,9 @@ namespace big
|
||||
lua_manager(folder scripts_folder, folder scripts_config_folder);
|
||||
~lua_manager();
|
||||
|
||||
void disable_all_modules();
|
||||
void enable_all_modules();
|
||||
|
||||
void load_all_modules();
|
||||
void unload_all_modules();
|
||||
|
||||
@ -39,13 +45,17 @@ namespace big
|
||||
}
|
||||
|
||||
std::weak_ptr<lua_module> get_module(rage::joaat_t module_id);
|
||||
std::weak_ptr<lua_module> get_disabled_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);
|
||||
|
||||
std::weak_ptr<lua_module> enable_module(rage::joaat_t module_id);
|
||||
std::weak_ptr<lua_module> disable_module(rage::joaat_t module_id);
|
||||
|
||||
void unload_module(rage::joaat_t module_id);
|
||||
void load_module(const std::filesystem::path& module_path);
|
||||
std::weak_ptr<lua_module> load_module(const std::filesystem::path& module_path);
|
||||
|
||||
void reload_changed_scripts();
|
||||
|
||||
@ -97,6 +107,16 @@ namespace big
|
||||
func(module);
|
||||
}
|
||||
}
|
||||
|
||||
inline void for_each_disabled_module(auto func)
|
||||
{
|
||||
std::lock_guard guard(m_disabled_module_lock);
|
||||
|
||||
for (auto& module : m_disabled_modules)
|
||||
{
|
||||
func(module);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline lua_manager* g_lua_manager;
|
||||
|
@ -80,38 +80,42 @@ namespace big
|
||||
return sol::stack::push(L, msg);
|
||||
}
|
||||
|
||||
lua_module::lua_module(const std::filesystem::path& module_path, folder& scripts_folder) :
|
||||
lua_module::lua_module(const std::filesystem::path& module_path, folder& scripts_folder, bool disabled) :
|
||||
m_state(),
|
||||
m_module_path(module_path),
|
||||
m_module_name(module_path.filename().string()),
|
||||
m_module_id(rage::joaat(m_module_name))
|
||||
m_module_id(rage::joaat(m_module_name)),
|
||||
m_disabled(disabled)
|
||||
{
|
||||
// clang-format off
|
||||
m_state.open_libraries(
|
||||
sol::lib::base,
|
||||
sol::lib::package,
|
||||
sol::lib::coroutine,
|
||||
sol::lib::string,
|
||||
sol::lib::os,
|
||||
sol::lib::math,
|
||||
sol::lib::table,
|
||||
sol::lib::bit32,
|
||||
sol::lib::io,
|
||||
sol::lib::utf8
|
||||
);
|
||||
// clang-format on
|
||||
if (!m_disabled)
|
||||
{
|
||||
// clang-format off
|
||||
m_state.open_libraries(
|
||||
sol::lib::base,
|
||||
sol::lib::package,
|
||||
sol::lib::coroutine,
|
||||
sol::lib::string,
|
||||
sol::lib::os,
|
||||
sol::lib::math,
|
||||
sol::lib::table,
|
||||
sol::lib::bit32,
|
||||
sol::lib::io,
|
||||
sol::lib::utf8
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
init_lua_api(scripts_folder);
|
||||
init_lua_api(scripts_folder);
|
||||
|
||||
m_state["!module_name"] = m_module_name;
|
||||
m_state["!this"] = this;
|
||||
m_state["!module_name"] = m_module_name;
|
||||
m_state["!this"] = this;
|
||||
|
||||
m_state.set_exception_handler(exception_handler);
|
||||
m_state.set_panic(sol::c_call<decltype(&panic_handler), &panic_handler>);
|
||||
lua_CFunction traceback_function = sol::c_call<decltype(&traceback_error_handler), &traceback_error_handler>;
|
||||
sol::protected_function::set_default_handler(sol::object(m_state.lua_state(), sol::in_place, traceback_function));
|
||||
m_state.set_exception_handler(exception_handler);
|
||||
m_state.set_panic(sol::c_call<decltype(&panic_handler), &panic_handler>);
|
||||
lua_CFunction traceback_function = sol::c_call<decltype(&traceback_error_handler), &traceback_error_handler>;
|
||||
sol::protected_function::set_default_handler(sol::object(m_state.lua_state(), sol::in_place, traceback_function));
|
||||
|
||||
m_last_write_time = std::filesystem::last_write_time(m_module_path);
|
||||
m_last_write_time = std::filesystem::last_write_time(m_module_path);
|
||||
}
|
||||
}
|
||||
|
||||
lua_module::~lua_module()
|
||||
@ -150,6 +154,11 @@ namespace big
|
||||
return m_last_write_time;
|
||||
}
|
||||
|
||||
const bool lua_module::is_disabled() const
|
||||
{
|
||||
return m_disabled;
|
||||
}
|
||||
|
||||
void lua_module::set_folder_for_lua_require(folder& scripts_folder)
|
||||
{
|
||||
std::string scripts_search_path = scripts_folder.get_path().string() + "/?.lua;";
|
||||
|
@ -21,6 +21,7 @@ namespace big
|
||||
|
||||
std::chrono::time_point<std::chrono::file_clock> m_last_write_time;
|
||||
|
||||
bool m_disabled;
|
||||
std::mutex m_registered_scripts_mutex;
|
||||
|
||||
public:
|
||||
@ -36,7 +37,7 @@ namespace big
|
||||
std::unordered_map<menu_event, std::vector<sol::protected_function>> m_event_callbacks;
|
||||
std::vector<void*> m_allocated_memory;
|
||||
|
||||
lua_module(const std::filesystem::path& module_path, folder& scripts_folder);
|
||||
lua_module(const std::filesystem::path& module_path, folder& scripts_folder, bool disabled = false);
|
||||
~lua_module();
|
||||
|
||||
const std::filesystem::path& module_path() const;
|
||||
@ -44,6 +45,7 @@ namespace big
|
||||
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;
|
||||
const bool is_disabled() const;
|
||||
|
||||
// used for sandboxing and limiting to only our custom search path for the lua require function
|
||||
void set_folder_for_lua_require(folder& scripts_folder);
|
||||
|
Reference in New Issue
Block a user