From 9823ca7c4a802578880e8ca86c3665645a918138 Mon Sep 17 00:00:00 2001 From: PJB3005 Date: Fri, 15 May 2026 23:37:23 +0200 Subject: [PATCH] Split native mod stuff out of LoadedMod --- include/dusk/mod_loader.hpp | 21 ++-- src/dusk/modding/mod_loader.cpp | 200 +++++++++++++++++--------------- 2 files changed, 118 insertions(+), 103 deletions(-) diff --git a/include/dusk/mod_loader.hpp b/include/dusk/mod_loader.hpp index 13624ee6eb..d48875b4ff 100644 --- a/include/dusk/mod_loader.hpp +++ b/include/dusk/mod_loader.hpp @@ -31,14 +31,9 @@ struct ModMetadata { std::string description; }; -struct LoadedMod { - ModMetadata metadata; - std::string mod_path; - std::string dir; - +struct NativeMod { std::unique_ptr handle; - bool active = false; - bool load_failed = false; + DuskModAPI api{}; using FnInit = void (*)(DuskModAPI*); using FnTick = void (*)(DuskModAPI*); @@ -47,8 +42,17 @@ struct LoadedMod { FnInit fn_init = nullptr; FnTick fn_tick = nullptr; FnCleanup fn_cleanup = nullptr; +}; - DuskModAPI api{}; +struct LoadedMod { + ModMetadata metadata; + std::string mod_path; + std::string dir; + + bool active = false; + bool load_failed = false; + + std::unique_ptr native; std::unique_ptr bundle; std::vector tab_content; @@ -72,6 +76,7 @@ private: bool m_initialized = false; void tryLoadDusk(const std::filesystem::path& modPath, bool fromDir); + bool tryLoadNativeMod(LoadedMod& mod); void buildAPI(LoadedMod& mod); void initOverlayFiles(); }; diff --git a/src/dusk/modding/mod_loader.cpp b/src/dusk/modding/mod_loader.cpp index c222e33d68..598b44e79d 100644 --- a/src/dusk/modding/mod_loader.cpp +++ b/src/dusk/modding/mod_loader.cpp @@ -302,31 +302,32 @@ ModLoader& ModLoader::instance() { } void ModLoader::buildAPI(LoadedMod& mod) { - mod.api.api_version = DUSK_MOD_API_VERSION; - mod.api.mod_dir = mod.dir.c_str(); - mod.api.log_info = cb_log_info; - mod.api.log_warn = cb_log_warn; - mod.api.log_error = cb_log_error; - mod.api.load_resource = cb_load_resource; - mod.api.free_resource = cb_free_resource; - mod.api.register_tab_content = cb_register_tab_content; - mod.api.register_tab_update = cb_register_tab_update; - mod.api.panel_add_section = cb_panel_add_section; - mod.api.panel_add_button = cb_panel_add_button; - mod.api.panel_add_badge_row = cb_panel_add_badge_row; - mod.api.panel_add_dyn_text = cb_panel_add_dyn_text; - mod.api.elem_set_badge = cb_elem_set_badge; - mod.api.elem_set_text = cb_elem_set_text; - mod.api.panel_add_progress = cb_panel_add_progress; - mod.api.elem_set_progress = cb_elem_set_progress; - mod.api.hook_install = hookInstallByAddr; - mod.api.hook_pre = api_hook_pre; - mod.api.hook_post = api_hook_post; - mod.api.hook_replace = api_hook_replace; - mod.api.hook_dispatch_pre = hookDispatchPre; - mod.api.hook_dispatch_post = hookDispatchPost; - mod.api.service_publish = cb_service_publish; - mod.api.service_get = cb_service_get; + auto& native = *mod.native; + native.api.api_version = DUSK_MOD_API_VERSION; + native.api.mod_dir = mod.dir.c_str(); + native.api.log_info = cb_log_info; + native.api.log_warn = cb_log_warn; + native.api.log_error = cb_log_error; + native.api.load_resource = cb_load_resource; + native.api.free_resource = cb_free_resource; + native.api.register_tab_content = cb_register_tab_content; + native.api.register_tab_update = cb_register_tab_update; + native.api.panel_add_section = cb_panel_add_section; + native.api.panel_add_button = cb_panel_add_button; + native.api.panel_add_badge_row = cb_panel_add_badge_row; + native.api.panel_add_dyn_text = cb_panel_add_dyn_text; + native.api.elem_set_badge = cb_elem_set_badge; + native.api.elem_set_text = cb_elem_set_text; + native.api.panel_add_progress = cb_panel_add_progress; + native.api.elem_set_progress = cb_elem_set_progress; + native.api.hook_install = hookInstallByAddr; + native.api.hook_pre = api_hook_pre; + native.api.hook_post = api_hook_post; + native.api.hook_replace = api_hook_replace; + native.api.hook_dispatch_pre = hookDispatchPre; + native.api.hook_dispatch_post = hookDispatchPost; + native.api.service_publish = cb_service_publish; + native.api.service_get = cb_service_get; } static std::unique_ptr loadBundle(const std::filesystem::path& modPath, bool fromDir) { @@ -408,6 +409,77 @@ static bool checkDuplicateMod(const ModMetadata& metadata, const std::vector dllData; + try { + dllData = mod.bundle->readFile(dllEntry); + } catch (const std::runtime_error& e) { + DuskLog.error( + "ModLoader: failed to extract {} from {}", dllEntry, mod.metadata.id); + return false; + } + + { + std::ofstream out(dllCachePath, std::ios::binary | std::ios::out); + if (!out) { + DuskLog.error("ModLoader: failed to write {}", io::fs_path_to_string(dllCachePath)); + return false; + } + + out.write( + reinterpret_cast(dllData.data()), + static_cast(dllData.size())); + } + + auto nativeMod = std::make_unique(); + try { + nativeMod->handle = std::make_unique(dllCachePath); + } catch (const std::runtime_error& e) { + DuskLog.error("ModLoader: failed to open {}: {}", io::fs_path_to_string(dllCachePath), e.what()); + return false; + } + + const auto mod_api_ver = nativeMod->handle->LookupSymbol("mod_api_version"); + if (mod_api_ver && *mod_api_ver != DUSK_MOD_API_VERSION) { + DuskLog.error("ModLoader: {} expects API v{} but engine is v{}, skipping", + io::fs_path_to_string(fs::path(dllEntry).filename()), *mod_api_ver, DUSK_MOD_API_VERSION); + return false; + } + + nativeMod->fn_init = nativeMod->handle->LookupSymbol("mod_init"); + nativeMod->fn_tick = nativeMod->handle->LookupSymbol("mod_tick"); + nativeMod->fn_cleanup = nativeMod->handle->LookupSymbol("mod_cleanup"); + + if (!nativeMod->fn_init || !nativeMod->fn_tick) { + DuskLog.error("ModLoader: {} missing mod_init or mod_tick — skipping", + io::fs_path_to_string(fs::path(dllEntry).filename())); + return false; + } + + mod.dir = io::fs_path_to_string(fs::absolute(cacheDir)); + mod.native = std::move(nativeMod); + return true; +} + void ModLoader::tryLoadDusk(const std::filesystem::path& modPath, bool fromDir) { namespace fs = std::filesystem; @@ -438,77 +510,15 @@ void ModLoader::tryLoadDusk(const std::filesystem::path& modPath, bool fromDir) return; } - auto [dllEntry, dllFallback] = LocateDllInBundle(*bundle); - if (dllEntry.empty()) { - dllEntry = dllFallback; - } - - if (dllEntry.empty()) { - DuskLog.warn( - "ModLoader: no *{} found in {} — skipping", NativeModule::LibraryExtension, io::fs_path_to_string(modPath.filename())); - return; - } - - const fs::path cacheDir = m_modsDir / ".cache" / modPath.stem(); - std::error_code ec; - fs::create_directories(cacheDir, ec); - - const fs::path dllCachePath = cacheDir / fs::path(dllEntry).filename(); - - std::vector dllData; - try { - dllData = bundle->readFile(dllEntry); - } catch (const std::runtime_error& e) { - DuskLog.error( - "ModLoader: failed to extract {} from {}", dllEntry, io::fs_path_to_string(modPath.filename())); - return; - } - - { - std::ofstream out(dllCachePath, std::ios::binary | std::ios::out); - if (!out) { - DuskLog.error("ModLoader: failed to write {}", io::fs_path_to_string(dllCachePath)); - return; - } - - out.write( - reinterpret_cast(dllData.data()), - static_cast(dllData.size())); - } - - NativeModule native; - try { - native = NativeModule(dllCachePath); - } catch (const std::runtime_error& e) { - DuskLog.error("ModLoader: failed to open {}: {}", io::fs_path_to_string(dllCachePath), e.what()); - return; - } - LoadedMod mod; mod.mod_path = io::fs_path_to_string(fs::absolute(modPath)); - mod.dir = io::fs_path_to_string(fs::absolute(cacheDir)); - mod.handle = std::make_unique(std::move(native)); - const auto mod_api_ver = mod.handle->LookupSymbol("mod_api_version"); - if (mod_api_ver && *mod_api_ver != DUSK_MOD_API_VERSION) { - DuskLog.error("ModLoader: {} expects API v{} but engine is v{}, skipping", - io::fs_path_to_string(fs::path(dllEntry).filename()), *mod_api_ver, DUSK_MOD_API_VERSION); - return; - } - - mod.fn_init = mod.handle->LookupSymbol("mod_init"); - mod.fn_tick = mod.handle->LookupSymbol("mod_tick"); - mod.fn_cleanup = mod.handle->LookupSymbol("mod_cleanup"); - - if (!mod.fn_init || !mod.fn_tick) { - DuskLog.error("ModLoader: {} missing mod_init or mod_tick — skipping", - io::fs_path_to_string(fs::path(dllEntry).filename())); - return; - } - mod.metadata = std::move(metadata); - mod.bundle = std::move(bundle); + if (!tryLoadNativeMod(mod)) { + return; + } + const auto& inserted = m_mods.emplace_back(std::move(mod)); DuskLog.info( @@ -567,7 +577,7 @@ void ModLoader::init() { for (auto& mod : m_mods) { ModGuard guard(&mod); try { - mod.fn_init(&mod.api); + mod.native->fn_init(&mod.native->api); if (!mod.load_failed) { mod.active = true; DuskLog.info("ModLoader: '{}' initialized", mod.metadata.id); @@ -593,7 +603,7 @@ void ModLoader::tick() { } ModGuard guard(&mod); try { - mod.fn_tick(&mod.api); + mod.native->fn_tick(&mod.native->api); } catch (const std::exception& e) { DuskLog.error( "ModLoader: exception in {}.mod_tick(): {} — disabling", mod.metadata.id, e.what()); @@ -608,10 +618,10 @@ void ModLoader::tick() { void ModLoader::shutdown() { for (auto& mod : m_mods) { hookClearMod(&mod); - if (mod.fn_cleanup) { + if (mod.native->fn_cleanup) { ModGuard guard(&mod); try { - mod.fn_cleanup(&mod.api); + mod.native->fn_cleanup(&mod.native->api); } catch (...) { } }