#pragma once #include #include #include #include "dusk/mod_api.h" namespace dusk::modding { class ModBundle; class NativeModule; } namespace dusk { struct RmlTabContentCallback { void (*build_fn)(void* panel, void* userdata); void* userdata; }; struct RmlTabUpdateCallback { void (*update_fn)(void* userdata); void* userdata; }; struct ModMetadata { std::string id; std::string name; std::string version; std::string author; std::string description; bool hasCode; }; struct NativeMod { std::unique_ptr handle; DuskModAPI api{}; using FnInit = void (*)(DuskModAPI*); using FnTick = void (*)(DuskModAPI*); using FnCleanup = void (*)(DuskModAPI*); FnInit fn_init = nullptr; FnTick fn_tick = nullptr; FnCleanup fn_cleanup = nullptr; }; 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; std::vector tab_updates; }; class ModLoader { public: static ModLoader& instance(); void setModsDir(std::filesystem::path dir) { m_modsDir = std::move(dir); } void init(); void tick(); void shutdown(); const std::vector& mods() const { return m_mods; } private: std::vector m_mods; std::filesystem::path m_modsDir; bool m_initialized = false; void tryLoadDusk(const std::filesystem::path& modPath, bool fromDir); bool tryLoadNativeMod(LoadedMod& mod); void buildAPI(LoadedMod& mod); void initOverlayFiles(); }; } // namespace dusk