diff --git a/BG3Extender/Extender/Client/ExtensionStateClient.h b/BG3Extender/Extender/Client/ExtensionStateClient.h index 9a1a49986..5acd71fc8 100644 --- a/BG3Extender/Extender/Client/ExtensionStateClient.h +++ b/BG3Extender/Extender/Client/ExtensionStateClient.h @@ -36,6 +36,11 @@ class ExtensionState : public ExtensionStateBase void OnInputEvent(SDL_Event* event, int& result); + inline char const * GetUnconditionalFileName() override + { + return "OverrideClient.lua"; + } + protected: friend LuaStatePin; std::unique_ptr Lua; @@ -48,4 +53,4 @@ class ExtensionState : public ExtensionStateBase void FireInputEvents(); }; -END_NS() \ No newline at end of file +END_NS() diff --git a/BG3Extender/Extender/Server/ExtensionStateServer.h b/BG3Extender/Extender/Server/ExtensionStateServer.h index 6c3d56300..96740dc54 100644 --- a/BG3Extender/Extender/Server/ExtensionStateServer.h +++ b/BG3Extender/Extender/Server/ExtensionStateServer.h @@ -27,6 +27,11 @@ namespace bg3se::esv return "BootstrapServer.lua"; } + inline char const * GetUnconditionalFileName() override + { + return "OverrideServer.lua"; + } + inline std::unordered_set const& GetPersistentStats() const { return persistentStats_; @@ -63,4 +68,4 @@ namespace bg3se::esv void DoLuaReset() override; void LuaStartup() override; }; -} \ No newline at end of file +} diff --git a/BG3Extender/Extender/Shared/ExtensionState.cpp b/BG3Extender/Extender/Shared/ExtensionState.cpp index 3e6982ae9..fd1dfd26a 100644 --- a/BG3Extender/Extender/Shared/ExtensionState.cpp +++ b/BG3Extender/Extender/Shared/ExtensionState.cpp @@ -53,8 +53,21 @@ namespace bg3se return; } + std::unordered_set mods; + + auto isLoaded = [&mods, &modManager](const Module& mod){ + if (mods.size() == 0) + { + for (const Module& mod : modManager->BaseModule.LoadOrderedModules) + { + mods.emplace(mod.Info.ModuleUUIDString); + } + } + return mods.contains(mod.Info.ModuleUUIDString); + }; + unsigned numConfigs{ 0 }; - for (auto const& mod : modManager->LoadOrderedModules) { + for (auto const& mod : modManager->AvailableMods) { auto dir = mod.Info.Directory; auto configFile = "Mods/" + dir + "/ScriptExtender/Config.json"; auto reader = GetStaticSymbols().MakeFileReader(configFile); @@ -82,6 +95,8 @@ namespace bg3se continue; } + config.IsLoaded = isLoaded(mod); + if (config.MinimumVersion > CurrentVersion) { OsiError("Module '" << mod.Info.Name << " is targeting version v" << config.MinimumVersion << " that's more recent than the current version!"); } @@ -524,7 +539,7 @@ namespace bg3se } lua::Restriction restriction(*lua, lua::State::RestrictAll); - for (auto const& mod : modManager->LoadOrderedModules) { + for (auto const& mod : modManager->AvailableMods) { auto configIt = modConfigs_.find(mod.Info.ModuleUUIDString); if (configIt != modConfigs_.end()) { auto const & config = configIt->second; @@ -536,9 +551,17 @@ namespace bg3se } if (context_ == ExtensionStateContext::Game) { - LuaLoadGameBootstrap(config, mod); + LuaLoadGameUnconditional(config, mod); + if (config.IsLoaded) + { + LuaLoadGameBootstrap(config, mod); + } } else if (context_ == ExtensionStateContext::Load) { - LuaLoadPreinitBootstrap(config, mod); + LuaLoadPreinitUnconditional(config, mod); + if (config.IsLoaded) + { + LuaLoadPreinitBootstrap(config, mod); + } } else { ERR("Bootstrap request with Uninitialized extension context?"); } @@ -549,6 +572,48 @@ namespace bg3se lua->FinishStartup(); } + void ExtensionStateBase::LuaLoadPreinitUnconditional(ExtensionModConfig const& config, Module const& mod) + { + auto bootstrapFileName = "OverrideModule.lua"; + auto const& sym = GetStaticSymbols(); + + auto path = ResolveModScriptPath(mod, bootstrapFileName); + if (!sym.FileExists(path)) { + return; + } + + LuaVirtualPin lua(*this); + auto L = lua->GetState(); + lua::push(L, mod.Info.ModuleUUID); + lua_setglobal(L, "ModuleUUID"); + + OsiMsg("Loading preinit override script: " << path); + lua->LoadBootstrap(bootstrapFileName, config.ModTable); + + lua::push(L, nullptr); + lua_setglobal(L, "ModuleUUID"); + } + + void ExtensionStateBase::LuaLoadGameUnconditional(ExtensionModConfig const& config, Module const& mod) + { + auto bootstrapFileName = GetUnconditionalFileName(); + auto const& sym = GetStaticSymbols(); + + auto bootstrapPath = ResolveModScriptPath(mod, bootstrapFileName); + if (!bootstrapPath.empty() && sym.FileExists(bootstrapPath)) { + LuaVirtualPin lua(*this); + auto L = lua->GetState(); + lua::push(L, mod.Info.ModuleUUIDString); + lua_setglobal(L, "ModuleUUID"); + + OsiMsg("Loading override script: " << bootstrapPath); + lua->LoadBootstrap(bootstrapFileName, config.ModTable); + + lua::push(L, nullptr); + lua_setglobal(L, "ModuleUUID"); + } + } + void ExtensionStateBase::LuaLoadGameBootstrap(ExtensionModConfig const& config, Module const& mod) { auto bootstrapFileName = GetBootstrapFileName(); diff --git a/BG3Extender/Extender/Shared/ExtensionState.h b/BG3Extender/Extender/Shared/ExtensionState.h index 8dad08c5b..8bd623788 100644 --- a/BG3Extender/Extender/Shared/ExtensionState.h +++ b/BG3Extender/Extender/Shared/ExtensionState.h @@ -17,6 +17,8 @@ namespace bg3se // Name to use in Lua Mods global table (>= v43) STDString ModTable; std::unordered_set FeatureFlags; + + bool IsLoaded; }; enum class ExtensionStateContext @@ -46,8 +48,9 @@ namespace bg3se virtual void Reset(); virtual lua::State * GetLua() = 0; virtual ModManager * GetModManager() = 0; - virtual LevelManager* GetLevelManager() = 0; + virtual LevelManager * GetLevelManager() = 0; virtual char const * GetBootstrapFileName() = 0; + virtual char const * GetUnconditionalFileName() = 0; void LoadConfigs(); bool LoadConfig(Module const & mod, STDString const & configText, ExtensionModConfig & config); @@ -153,6 +156,8 @@ namespace bg3se void LuaResetInternal(); virtual void DoLuaReset() = 0; virtual void LuaStartup(); + void LuaLoadPreinitUnconditional(ExtensionModConfig const& config, Module const& mod); + void LuaLoadGameUnconditional(ExtensionModConfig const& config, Module const& mod); void LuaLoadGameBootstrap(ExtensionModConfig const& config, Module const& mod); void LuaLoadPreinitBootstrap(ExtensionModConfig const& config, Module const& mod); }; @@ -262,4 +267,4 @@ namespace bg3se T & state_; }; -} \ No newline at end of file +} diff --git a/BG3Extender/Extender/Shared/StatLoadOrderHelper.inl b/BG3Extender/Extender/Shared/StatLoadOrderHelper.inl index dc954b176..e4013249d 100644 --- a/BG3Extender/Extender/Shared/StatLoadOrderHelper.inl +++ b/BG3Extender/Extender/Shared/StatLoadOrderHelper.inl @@ -24,8 +24,8 @@ void StatLoadOrderHelper::UpdateModDirectoryMap() auto modManager = gExtender->GetCurrentExtensionState()->GetModManager(); if (modManager) { - for (auto const& mod : modManager->LoadOrderedModules) { - modDirectoryToModMap_.insert(std::make_pair(mod.Info.Directory, mod.Info.ModuleUUIDString)); + for (auto const& mod : modManager->AvailableMods) { + modDirectoryToModMap_.insert(std::make_pair(mod.Info.Directory, mod.Info.ModuleUUIDString)); } } } diff --git a/BG3Extender/Extender/Shared/UserVariables.inl b/BG3Extender/Extender/Shared/UserVariables.inl index 14ae17ffb..c54099607 100644 --- a/BG3Extender/Extender/Shared/UserVariables.inl +++ b/BG3Extender/Extender/Shared/UserVariables.inl @@ -757,7 +757,7 @@ void ModVariableManager::OnSessionLoading() auto modManager = isServer_ ? GetStaticSymbols().GetModManagerServer() : GetStaticSymbols().GetModManagerClient(); if (modManager != nullptr) { uint32_t nextIndex{ 0 }; - for (auto const& mod : modManager->LoadOrderedModules) { + for (auto const& mod : modManager->AvailableMods) { modIndices_.set(mod.Info.ModuleUUID, nextIndex++); } } else { @@ -1291,9 +1291,9 @@ CachedModVariableManager::~CachedModVariableManager() Guid CachedModVariableManager::ModIndexToGuid(uint32_t modIndex) { if (isServer_) { - return GetStaticSymbols().GetModManagerServer()->LoadOrderedModules[modIndex].Info.ModuleUUID; + return GetStaticSymbols().GetModManagerServer()->AvailableMods[modIndex].Info.ModuleUUID; } else { - return GetStaticSymbols().GetModManagerClient()->LoadOrderedModules[modIndex].Info.ModuleUUID; + return GetStaticSymbols().GetModManagerClient()->AvailableMods[modIndex].Info.ModuleUUID; } } diff --git a/BG3Extender/GameDefinitions/GameHelpers.cpp b/BG3Extender/GameDefinitions/GameHelpers.cpp index 90213611d..f6e32f7d6 100644 --- a/BG3Extender/GameDefinitions/GameHelpers.cpp +++ b/BG3Extender/GameDefinitions/GameHelpers.cpp @@ -189,6 +189,12 @@ namespace bg3se } } + for (auto const& mod : AvailableMods) { + if (mod.Info.ModuleUUIDString == modUuidFS) { + return &mod; + } + } + return nullptr; } diff --git a/BG3Extender/Lua/Libs/Mod.inl b/BG3Extender/Lua/Libs/Mod.inl index 0417facfd..d5643701f 100644 --- a/BG3Extender/Lua/Libs/Mod.inl +++ b/BG3Extender/Lua/Libs/Mod.inl @@ -35,6 +35,48 @@ bool IsModLoaded(lua_State* L, FixedString modNameGuid) return false; } +/// +/// Returns whether the module with the specified GUID is available, i.e. in the mod directory. +/// +/// Example: +/// ```lua +/// if (Ext.IsModAvailable("5cc23efe-f451-c414-117d-b68fbc53d32d")) then +/// Ext.Print("Mod available") +/// end +/// ``` +/// +/// UUID of mod to check +bool IsModAvailable(char const* modNameGuid) +{ + auto modUuid = Guid::Parse(modNameGuid); + if (modUuid) { + auto modManager = gExtender->GetCurrentExtensionState()->GetModManager(); + for (auto const& mod : modManager->AvailableMods) { + if (mod.Info.ModuleUUID == *modUuid) { + return true; + } + } + } + + return false; +} + +/// +/// Returns the list of available module UUIDs. +/// +/// +ObjectSet GetAvailableMods() +{ + ObjectSet mods; + auto modManager = gExtender->GetCurrentExtensionState()->GetModManager(); + + for (auto const& mod : modManager->AvailableMods) { + mods.Add(mod.Info.ModuleUUID); + } + + return mods; +} + /// /// Returns the list of loaded module UUIDs in the order they're loaded in. /// @@ -52,7 +94,7 @@ Array GetLoadOrder(lua_State* L) } /// -/// Returns detailed information about the specified (loaded) module. +/// Returns detailed information about the specified (available) module. /// /// Mod UUID to query Module* GetMod(lua_State* L, FixedString modNameGuid) @@ -61,7 +103,7 @@ Module* GetMod(lua_State* L, FixedString modNameGuid) auto modUuid = Guid::Parse(modNameGuid.GetStringView()); if (modUuid) { - for (auto& mod : modManager->LoadOrderedModules) { + for (auto& mod : modManager->AvailableMods) { if (mod.Info.ModuleUUID == *modUuid) { return &mod; } @@ -92,7 +134,9 @@ void RegisterModLib() DECLARE_MODULE(Mod, Both) BEGIN_MODULE() MODULE_FUNCTION(IsModLoaded) + MODULE_FUNCTION(IsModAvailable) MODULE_FUNCTION(GetLoadOrder) + MODULE_FUNCTION(GetAvailableMods) MODULE_FUNCTION(GetMod) MODULE_FUNCTION(GetBaseMod) MODULE_FUNCTION(GetModManager)