From 1df8d10ea7e339247be3be7830001f9fd81281a5 Mon Sep 17 00:00:00 2001 From: xiaoxiao921 Date: Tue, 23 Apr 2024 20:23:31 +0200 Subject: [PATCH] lua: completly butchered but seemingly working barebone implementation --- src/gui/gui.cpp | 17 ++++ src/hades2/lua/sgg_lua.cpp | 34 -------- src/hades2/lua/sgg_lua.hpp | 4 - src/hooks/hooking.cpp | 169 ++++++++++++++++++++++++++++++++++++- src/lua/bindings/imgui.hpp | 37 +++++++- src/lua/bindings/paths.cpp | 4 + src/lua/lua_manager.cpp | 85 +++++++++++-------- src/lua/lua_manager.hpp | 10 ++- src/lua/lua_module.cpp | 14 ++- 9 files changed, 291 insertions(+), 83 deletions(-) diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 364578d..8ccae64 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -120,6 +120,23 @@ namespace big void gui::dx_on_tick() { + std::scoped_lock l(big::g_lua_manager_mutex); + if (g_is_lua_state_valid) + { + sol::state_view lua(*g_pointers->m_hades2.m_lua_state); + + for (const auto& f : g_lua_imgui_callbacks) + { + auto res = f(); + if (!res.valid()) + { + LOG(FATAL) << "Failed executing imgui callback: "; + } + } + } + + return; + if (!g_lua_manager) { return; diff --git a/src/hades2/lua/sgg_lua.cpp b/src/hades2/lua/sgg_lua.cpp index ea79eb5..67fa788 100644 --- a/src/hades2/lua/sgg_lua.cpp +++ b/src/hades2/lua/sgg_lua.cpp @@ -1,40 +1,6 @@ #include "sgg_lua.hpp" -#include "threads/util.hpp" - -#include -#include -#include -#include - namespace big { - static std::unique_ptr lua_manager_instance; - - void hook_luaL_checkversion_(lua_State* L, lua_Number ver) - { - } - - void hook_sgg_ScriptManager_Clear() - { - threads::suspend_all_but_one(); - - lua_manager_instance.reset(); - LOG(INFO) << "Lua manager reset."; - - big::g_hooking->get_original()(); - } - - void hook_InitLua() - { - big::g_hooking->get_original()(); - - lua_manager_instance = std::make_unique(*g_pointers->m_hades2.m_lua_state, - g_file_manager.get_project_folder("config"), - g_file_manager.get_project_folder("plugins_data"), - g_file_manager.get_project_folder("plugins")); - LOG(INFO) << "Lua manager initialized."; - threads::resume_all(); - } } // namespace big diff --git a/src/hades2/lua/sgg_lua.hpp b/src/hades2/lua/sgg_lua.hpp index 76038b8..7d5a130 100644 --- a/src/hades2/lua/sgg_lua.hpp +++ b/src/hades2/lua/sgg_lua.hpp @@ -2,9 +2,5 @@ namespace big { - void hook_luaL_checkversion_(lua_State* L, lua_Number ver); - void hook_InitLua(); - - void hook_sgg_ScriptManager_Clear(); } // namespace big diff --git a/src/hooks/hooking.cpp b/src/hooks/hooking.cpp index 4046651..3d3ebd4 100644 --- a/src/hooks/hooking.cpp +++ b/src/hooks/hooking.cpp @@ -9,8 +9,141 @@ #include "pointers.hpp" #include "threads/util.hpp" +#include +#include +#include +#include +#include +#include +#include + namespace big { + static void hook_tg(void* this_, __int64 arg) + { + { + std::scoped_lock l(g_lua_manager_mutex); + } + { + big::g_hooking->get_original()(this_, arg); + } + { + std::scoped_lock l(g_lua_manager_mutex); + } + } + + static void hook_log_write(char level, const char* filename, int line_number, const char* message, ...) + { + va_list args; + + va_start(args, message); + int size = vsnprintf(nullptr, 0, message, args); + va_end(args); + + // Allocate a buffer to hold the formatted string + std::string result(size + 1, '\0'); // +1 for the null terminator + + // Format the string into the buffer + va_start(args, message); + vsnprintf(&result[0], size + 1, message, args); + va_end(args); + + big::g_hooking->get_original()(level, filename, line_number, result.c_str()); + + result.pop_back(); + + const char* levelStr; + switch (level) + { + case 8: levelStr = "WARN"; break; + case 4: levelStr = "INFO"; break; + case 2: levelStr = "DBG"; break; + case 16: levelStr = "ERR"; break; + default: levelStr = "UNK"; break; + } + + if (strlen(filename) > 41) + { + LOG(INFO) << "[" << levelStr << "] [" << (filename + 41) << ":" << line_number << "] " << result; + } + else + { + LOG(INFO) << "[" << levelStr << "] [" << filename << ":" << line_number << "] " << result; + } + } + + static std::unique_ptr g_lua_state_view; + + void delete_everything() + { + std::scoped_lock l(g_lua_manager_mutex); + + g_is_lua_state_valid = false; + g_lua_imgui_callbacks.clear(); + g_lua_state_view.reset(); + LOG(FATAL) << "state is no longer valid!"; + } + + int the_state_is_going_down(lua_State* L) + { + delete_everything(); + + return 0; + } + + void hook_in(lua_State* L) + { + std::scoped_lock l(g_lua_manager_mutex); + + g_lua_state_view = std::make_unique(L); + + const std::string my_inscrutable_key = + "..catchy_id.\xF0\x9F\x8F\xB4 \xF0\x9F\x8F\xB4 \xF0\x9F\x8F\xB4 \xF0\x9F\x8F\xB4 \xF0\x9F\x8F\xB4"; + sol::table my_takedown_metatable = g_lua_state_view->create_table_with(); + my_takedown_metatable[sol::meta_function::garbage_collect] = the_state_is_going_down; + sol::table my_takedown_table = g_lua_state_view->create_named_table(my_inscrutable_key, sol::metatable_key, my_takedown_metatable); + + sol::table g_table = g_lua_state_view->globals(); + //lua::gui::bind(g_table); + g_table.create_named("gui").set_function("add_imgui", + [](sol::protected_function cb) + { + g_lua_imgui_callbacks.push_back(cb); + }); + lua::imgui::bind(g_table); + //lua::log::bind(g_table); + + const auto script_folder = g_file_manager.get_project_folder("plugins"); + for (const auto& entry : std::filesystem::recursive_directory_iterator(script_folder.get_path(), std::filesystem::directory_options::skip_permission_denied)) + { + if (!entry.exists() || entry.path().extension() != ".lua") + { + continue; + } + + auto result = g_lua_state_view->safe_script_file((char*)entry.path().u8string().c_str(), &sol::script_pass_on_error, sol::load_mode::text); + + if (!result.valid()) + { + LOG(FATAL) << (char*)entry.path().u8string().c_str() << " failed to load: " << result.get().what(); + Logger::FlushQueue(); + } + } + + g_is_lua_state_valid = true; + LOG(FATAL) << "state is valid"; + } + + static char hook_sgg_ScriptManager_Load(const char* scriptFile) + { + if (!strcmp(scriptFile, "Main.lua")) + { + hook_in(*g_pointers->m_hades2.m_lua_state); + } + + return big::g_hooking->get_original()(scriptFile); + } + hooking::hooking() { for (auto& detour_hook_helper : m_detour_hook_helpers) @@ -22,15 +155,45 @@ namespace big } } - hooking::detour_hook_helper::add("Multiple Lua VM detected patch", luaL_checkversion_); - hooking::detour_hook_helper::add("LNS", g_pointers->m_hades2.m_init_lua); - hooking::detour_hook_helper::add("SMC", g_pointers->m_hades2.m_scriptmanager_clear); + hooking::detour_hook_helper::add("game logger", gmAddress::scan("8B D1 83 E2 08").offset(-0x2C).as()); hooking::detour_hook_helper::add("Suppress SGG BacktraceHandleException", g_pointers->m_hades2.m_sgg_BacktraceHandleException); hooking::detour_hook_helper::add("HSGGFRPEMAA", g_pointers->m_hades2.m_sgg_ForgeRenderer_PrintErrorMessageAndAssert); + // Lua stuff + { + // + hooking::detour_hook_helper::add( + "LC", + gmAddress::scan("49 3B DF 76 29").offset(-0x6E).as()); + + /*hooking::detour_hook_helper::add("Multiple Lua VM detected patch", luaL_checkversion_); + + hooking::detour_hook_helper::add("LNS", g_pointers->m_hades2.m_init_lua); + + hooking::detour_hook_helper::add( + "LC", + gmAddress::scan("E8 ? ? ? ? 44 89 3D ? ? ? ? 4C 8D 0D").offset(1).rip().as()); + + hooking::detour_hook_helper::add("SMC", g_pointers->m_hades2.m_scriptmanager_clear); + + + hooking::detour_hook_helper::add("SGG APP RES", g_pointers->m_hades2.m_sgg_app_reset); + + hooking::detour_hook_helper::add("worker thread lua safety", + gmAddress::scan("8B D5 49 8D 4E 10 E8").offset(-0x2A).as());*/ + + + //hooking::detour_hook_helper::add("lua safety 2", + //gmAddress::scan("33 C0 48 83 C4 28 C3 41").offset(-0x15).as()); + + //hooking::detour_hook_helper::add("try stuff 1", malloc); + //hooking::detour_hook_helper::add("try stuff 2", realloc); + //hooking::detour_hook_helper::add("try stuff 3", free); + } + g_hooking = this; } diff --git a/src/lua/bindings/imgui.hpp b/src/lua/bindings/imgui.hpp index 7881ed1..641024b 100644 --- a/src/lua/bindings/imgui.hpp +++ b/src/lua/bindings/imgui.hpp @@ -5,6 +5,11 @@ namespace lua::imgui { + inline bool Begin(const std::string& name) + { + return ImGui::Begin(name.c_str()); + } + // Windows inline bool Begin(const std::string& name, sol::this_environment env) { @@ -50,6 +55,11 @@ namespace lua::imgui return false; } + inline bool Begin(const std::string& name, int flags) + { + return ImGui::Begin(name.c_str(), nullptr, flags); + } + inline bool Begin(const std::string& name, int flags, sol::this_environment env) { if (flags & ImGuiWindowFlags_NoSavedSettings) @@ -99,6 +109,16 @@ namespace lua::imgui return false; } + inline std::tuple Begin(const std::string& name, bool open) + { + if (!open) + { + return std::make_tuple(false, false); + } + const bool shouldDraw = ImGui::Begin(name.c_str(), &open); + return std::make_tuple(open, open && shouldDraw); + } + inline std::tuple Begin(const std::string& name, bool open, sol::this_environment env) { if (!open) @@ -109,6 +129,16 @@ namespace lua::imgui return std::make_tuple(open, open && shouldDraw); } + inline std::tuple Begin(const std::string& name, bool open, int flags) + { + if (!open) + { + return std::make_tuple(false, false); + } + const bool shouldDraw = ImGui::Begin(name.c_str(), &open, flags); + return std::make_tuple(open, open && shouldDraw); + } + inline std::tuple Begin(const std::string& name, bool open, int flags, sol::this_environment env) { if (!open) @@ -3813,15 +3843,16 @@ namespace lua::imgui inline void bind(sol::table& state) { - InitUserType(state); - InitEnums(state); + //InitUserType(state); + //InitEnums(state); sol::table ImGui = state.create_named("ImGui"); #pragma region Windows ImGui.set_function("ShowDemoWindow", ShowDemoWindow); - ImGui.set_function("Begin", sol::overload(sol::resolve(Begin), sol::resolve(Begin), sol::resolve(const std::string&, bool, sol::this_environment)>(Begin), sol::resolve(const std::string&, bool, int, sol::this_environment)>(Begin))); + //ImGui.set_function("Begin", sol::overload(sol::resolve(Begin), sol::resolve(Begin), sol::resolve(const std::string&, bool, sol::this_environment)>(Begin), sol::resolve(const std::string&, bool, int, sol::this_environment)>(Begin))); + ImGui.set_function("Begin", sol::overload(sol::resolve(Begin), sol::resolve(Begin), sol::resolve(const std::string&, bool)>(Begin), sol::resolve(const std::string&, bool, int)>(Begin))); ImGui.set_function("End", End); #pragma endregion Windows diff --git a/src/lua/bindings/paths.cpp b/src/lua/bindings/paths.cpp index a382c76..2cb9ba4 100644 --- a/src/lua/bindings/paths.cpp +++ b/src/lua/bindings/paths.cpp @@ -16,6 +16,8 @@ namespace lua::paths // Used for data that must persist between sessions and that can be manipulated by the user. static std::string config() { + std::scoped_lock l(big::g_lua_manager_mutex); + return (char*)big::g_lua_manager->get_config_folder().get_path().u8string().c_str(); } @@ -26,6 +28,8 @@ namespace lua::paths // Used for data that must persist between sessions but not be manipulated by the user. static std::string plugins_data() { + std::scoped_lock l(big::g_lua_manager_mutex); + return (char*)big::g_lua_manager->get_plugins_data_folder().get_path().u8string().c_str(); } diff --git a/src/lua/lua_manager.cpp b/src/lua/lua_manager.cpp index 7d78086..0952f33 100644 --- a/src/lua/lua_manager.cpp +++ b/src/lua/lua_manager.cpp @@ -186,10 +186,20 @@ namespace big lua_manager::~lua_manager() { + LOG(WARNING) << "killin lua_mgr"; + lua::window::serialize(); unload_all_modules(); + sol::table xd = m_state.globals(); + xd["gui"] = sol::lua_nil; + xd["ImGui"] = sol::lua_nil; + xd["ImGuiKey"] = sol::lua_nil; + xd["ImGuiKeyMod"] = sol::lua_nil; + + m_state.collect_garbage(); + g_lua_manager = nullptr; } @@ -230,7 +240,7 @@ namespace big // When this function exits, Lua will exhibit default behavior and abort() } - void lua_manager::set_folder_for_lua_require() + /*void lua_manager::set_folder_for_lua_require() { std::string plugins_search_path = m_plugins_folder.get_path().string() + "/?.lua;"; @@ -260,9 +270,9 @@ namespace big sandbox_os["time"] = os["time"]; m_state["os"] = sandbox_os; - } + }*/ - template + /*template static constexpr auto not_supported_lua_function(const char (&function_name)[N]) { return [function_name](sol::this_environment env, sol::variadic_args args) @@ -352,10 +362,10 @@ namespace big if (!required_module_cache.contains(full_path)) { auto fresh_result = m_loadfile(full_path); - if (!fresh_result.valid() || fresh_result.get_type() == sol::type::nil /*LuaJIT*/) + if (!fresh_result.valid() || fresh_result.get_type() == sol::type::nil) { const auto error_msg = - !fresh_result.valid() ? fresh_result.get().what() : fresh_result.get(1) /*LuaJIT*/; + !fresh_result.valid() ? fresh_result.get().what() : fresh_result.get(1); LOG(FATAL) << "Failed require: " << error_msg; Logger::FlushQueue(); @@ -400,7 +410,7 @@ namespace big }; set_folder_for_lua_require(); - } + }*/ static int traceback_error_handler(lua_State* L) { @@ -424,26 +434,40 @@ namespace big void lua_manager::init_lua_state() { - m_state.set_exception_handler(exception_handler); + /*m_state.set_exception_handler(exception_handler); m_state.set_panic(sol::c_call); lua_CFunction traceback_function = sol::c_call; - sol::protected_function::set_default_handler(sol::object(m_state.lua_state(), sol::in_place, traceback_function)); + sol::protected_function::set_default_handler(sol::object(m_state.lua_state(), sol::in_place, traceback_function));*/ // clang-format off - m_state.open_libraries( + /*m_state.open_libraries( sol::lib::package, sol::lib::os, sol::lib::debug, sol::lib::io - ); + );*/ // clang-format on + /*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::debug, + sol::lib::bit32, + sol::lib::io, + sol::lib::utf8 + );*/ + init_lua_api(); } void lua_manager::init_lua_api() { - sol::table lua_ext = m_state.create_named_table(lua_ext_namespace); + /*sol::table lua_ext = m_state.create_named_table(lua_ext_namespace); sol::table mods = lua_ext.create_named("mods"); // Lua API: Function // Table: mods @@ -457,16 +481,20 @@ namespace big { mdl->m_data.m_on_all_mods_loaded_callbacks.push_back(cb); } - }; + };*/ + + sol::table xd = m_state.globals(); + lua::gui::bind(xd); + lua::imgui::bind(xd); // Let's keep that list sorted the same as the solution file explorer - lua::toml_lua::bind(lua_ext); + /*lua::toml_lua::bind(lua_ext); lua::gui::bind(lua_ext); lua::imgui::bind(lua_ext); lua::log::bind(m_state, lua_ext); lua::memory::bind(lua_ext); lua::path::bind(lua_ext); - lua::paths::bind(lua_ext); + lua::paths::bind(lua_ext);*/ } static void imgui_text(const char* fmt, const std::string& str) @@ -567,20 +595,24 @@ namespace big } } - const auto module = std::make_shared(module_info, m_state); - const auto load_result = module->load_and_call_plugin(m_state); + const auto module_index = m_modules.size(); + m_modules.push_back(std::make_unique(module_info, m_state)); + + const auto load_result = m_modules[module_index]->load_and_call_plugin(m_state); if (load_result == load_module_result::SUCCESS || (load_result == load_module_result::FAILED_TO_LOAD && ignore_failed_to_load)) { - m_modules.push_back(module); - if (m_is_all_mods_loaded) { - for (const auto& cb : module->m_data.m_on_all_mods_loaded_callbacks) + for (const auto& cb : m_modules[module_index]->m_data.m_on_all_mods_loaded_callbacks) { cb(); } } } + else + { + m_modules.pop_back(); + } return load_result; } @@ -600,21 +632,6 @@ namespace big return false; } - std::weak_ptr lua_manager::get_module(const std::string& module_guid) - { - std::lock_guard guard(m_module_lock); - - for (const auto& module : m_modules) - { - if (module->guid() == module_guid) - { - return module; - } - } - - return {}; - } - static bool topological_sort_visit(const std::string& node, std::stack& stack, std::vector& sorted_list, const std::function(const std::string&)>& dependency_selector, std::unordered_set& visited, std::unordered_set& sorted) { if (visited.contains(node)) diff --git a/src/lua/lua_manager.hpp b/src/lua/lua_manager.hpp index 935f9ad..ea51aa6 100644 --- a/src/lua/lua_manager.hpp +++ b/src/lua/lua_manager.hpp @@ -11,10 +11,10 @@ namespace big { private: sol::state_view m_state; - sol::protected_function m_loadfile; + //sol::protected_function m_loadfile; std::recursive_mutex m_module_lock; - std::vector> m_modules; + std::vector> m_modules; folder m_config_folder; folder m_plugins_data_folder; @@ -23,7 +23,7 @@ namespace big bool m_is_all_mods_loaded{}; public: - static constexpr auto lua_ext_namespace = "LuaExt"; + static constexpr auto lua_ext_namespace = "LuaExt2"; lua_manager(lua_State* game_lua_state, folder config_folder, folder plugins_data_folder, folder plugins_folder); ~lua_manager(); @@ -63,7 +63,6 @@ namespace big void draw_independent_gui(); bool module_exists(const std::string& module_guid); - std::weak_ptr get_module(const std::string& module_guid); void unload_module(const std::string& module_guid); load_module_result load_module(const module_info& module_info, bool ignore_failed_to_load = false); @@ -79,5 +78,8 @@ namespace big } }; + inline std::recursive_mutex g_lua_manager_mutex; + inline bool g_is_lua_state_valid = false; + inline std::vector g_lua_imgui_callbacks; inline lua_manager* g_lua_manager; } // namespace big diff --git a/src/lua/lua_module.cpp b/src/lua/lua_module.cpp index af7ebbb..f1d0fff 100644 --- a/src/lua/lua_module.cpp +++ b/src/lua/lua_module.cpp @@ -9,6 +9,8 @@ namespace big m_info(module_info), m_env(state, sol::create, state.globals()) { + //return; + // Lua API: Table // Name: _ENV - Plugin Specific Global Table // Each mod/plugin have their own global table containing helpers, such as: @@ -76,6 +78,15 @@ namespace big } m_data = {}; + + m_env["!guid"] = sol::lua_nil; + m_env["!config_mod_folder_path"] = sol::lua_nil; + m_env["!plugins_data_mod_folder_path"] = sol::lua_nil; + m_env["!plugins_mod_folder_path"] = sol::lua_nil; + m_env["!this"] = sol::lua_nil; + m_env = sol::lua_nil; + + LOG(FATAL) << "lua module data cleaned up"; } lua_module::~lua_module() @@ -122,7 +133,8 @@ namespace big // Table: mods // Field: [Mod GUID]: string // Each mod once loaded will have a key in this table, the key will be their guid string and the value their `_ENV`. - state.traverse_set(lua_manager::lua_ext_namespace, "mods", m_info.m_guid, m_env); + //state.traverse_set(lua_manager::lua_ext_namespace, "mods", m_info.m_guid, m_env); + //state.traverse_set("mods", m_info.m_guid, m_env); } return load_module_result::SUCCESS;