diff --git a/src/app/app.cpp b/src/app/app.cpp index 4f73ae16..54a14d07 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -520,7 +520,9 @@ bool CApplication::Create() GetLogger()->Warn("Config could not be loaded. Default values will be used!\n"); } - m_modManager->ReinitMods(); + m_modManager->FindMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); // Create the sound instance. #ifdef OPENAL_SOUND diff --git a/src/app/modman.cpp b/src/app/modman.cpp index b67853ce..ef044a8a 100644 --- a/src/app/modman.cpp +++ b/src/app/modman.cpp @@ -19,22 +19,18 @@ #include "app/modman.h" -//TODO: clean up includes #include "common/config.h" #include "app/app.h" #include "app/pathman.h" +#include "common/config_file.h" #include "common/logger.h" -#include "common/restext.h" -#include "common/settings.h" -#include "common/stringutils.h" #include "common/resources/resourcemanager.h" -#include "common/system/system.h" - #include +#include #include #include @@ -46,16 +42,64 @@ CModManager::CModManager(CApplication* app, CPathManager* pathManager) { } -void CModManager::ReinitMods() +void CModManager::FindMods() { m_mods.clear(); - const auto foundMods = m_pathManager->FindMods(); - for (const auto& modPath : foundMods) + + // Load names from the config file + std::vector savedModNames; + GetConfigFile().GetArrayProperty("Mods", "Names", savedModNames); + std::vector savedEnabled; + GetConfigFile().GetArrayProperty("Mods", "Enabled", savedEnabled); + + // Transform the data into Mod structures + m_mods.reserve(savedModNames.size()); + for (int i = 0; i < savedModNames.size(); ++i) { - Mod mod; - mod.name = boost::filesystem::path(modPath).stem().string(); - mod.path = modPath; - mod.enabled = m_pathManager->ModLoaded(mod.path); //TODO: load from some config file + Mod mod{}; + mod.name = savedModNames[i]; + if (i < savedEnabled.size()) + { + mod.enabled = savedEnabled[i]; + } + mod.path = ""; // Find the path later + m_mods.push_back(mod); + } + + // Search the folders for mods + const auto rawPaths = m_pathManager->FindMods(); + std::map modPaths; + for (const auto& path : rawPaths) + { + auto modName = boost::filesystem::path(path).stem().string(); + modPaths.insert(std::make_pair(modName, path)); + } + + // Find paths for already saved mods + auto it = m_mods.begin(); + while (it != m_mods.end()) + { + auto& mod = *it; + const auto pathsIt = modPaths.find(mod.name); + if (pathsIt != modPaths.end()) + { + mod.path = (*pathsIt).second; + modPaths.erase(pathsIt); + ++it; + } + else + { + GetLogger()->Warn("Could not find mod %s, removing it from the list\n", mod.name.c_str()); + it = m_mods.erase(it); + } + } + + // Add the remaining found mods to the end of the list + for (const auto& newMod : modPaths) + { + Mod mod{}; + mod.name = newMod.first; + mod.path = newMod.second; m_mods.push_back(mod); } } @@ -82,23 +126,38 @@ void CModManager::DisableMod(const std::string& modName) mod->enabled = false; } -void CModManager::ReloadMods() +void CModManager::UpdatePaths() { + m_pathManager->RemoveAllMods(); for (const auto& mod : m_mods) { - bool loaded = m_pathManager->ModLoaded(mod.path); - if (mod.enabled && !loaded) + if (mod.enabled) { m_pathManager->AddMod(mod.path); } - else if (!mod.enabled && loaded) - { - m_pathManager->RemoveMod(mod.path); - } } +} + +void CModManager::ReloadResources() +{ m_app->ReloadResources(); } +void CModManager::SaveMods() +{ + std::vector savedNames; + savedNames.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedNames), [](const Mod& mod) { return mod.name; }); + GetConfigFile().SetArrayProperty("Mods", "Names", savedNames); + + std::vector savedEnabled; + savedEnabled.reserve(m_mods.size()); + std::transform(m_mods.begin(), m_mods.end(), std::back_inserter(savedEnabled), [](const Mod& mod) { return mod.enabled; }); + GetConfigFile().SetArrayProperty("Mods", "Enabled", savedEnabled); + + GetConfigFile().Save(); +} + boost::optional CModManager::GetMod(const std::string& modName) { Mod* mod = FindMod(modName); diff --git a/src/app/modman.h b/src/app/modman.h index e1a47467..5ce3efa2 100644 --- a/src/app/modman.h +++ b/src/app/modman.h @@ -30,8 +30,8 @@ class CPathManager; struct Mod { - std::string name; - std::string path; + std::string name{}; + std::string path{}; bool enabled = false; //TODO: add metadata for UI }; @@ -41,6 +41,7 @@ struct Mod * \brief Main application * * This class handles the list of currently loaded mods. + * The order matters since the order in which files are loaded matters. * */ class CModManager @@ -49,7 +50,7 @@ public: CModManager(CApplication* app, CPathManager* pathManager); //! Finds all the mods along with their metadata - void ReinitMods(); + void FindMods(); //! Removes a mod from the list of loaded mods void EnableMod(const std::string& modName); @@ -58,7 +59,13 @@ public: void DisableMod(const std::string& modName); //! Reloads application resources so the enabled mods are applied - void ReloadMods(); + void ReloadResources(); + + //! Saves the current configuration of mods to the config file + void SaveMods(); + + //! Updates the paths in Path Manager according to the current mod configuration + void UpdatePaths(); boost::optional GetMod(const std::string& modName); const std::vector& GetMods() const; diff --git a/src/app/pathman.cpp b/src/app/pathman.cpp index 4c4a1a71..080c9b70 100644 --- a/src/app/pathman.cpp +++ b/src/app/pathman.cpp @@ -73,17 +73,30 @@ void CPathManager::AddMod(const std::string &modPath) { GetLogger()->Info("Loading mod: '%s'\n", modPath.c_str()); CResourceManager::AddLocation(modPath, true); + m_mods.push_back(modPath); } void CPathManager::RemoveMod(const std::string &modPath) { GetLogger()->Info("Unloading mod: '%s'\n", modPath.c_str()); CResourceManager::RemoveLocation(modPath); + auto it = std::find(m_mods.cbegin(), m_mods.cend(), modPath); + if (it != m_mods.cend()) + m_mods.erase(it); +} + +void CPathManager::RemoveAllMods() +{ + for (const auto& modPath : m_mods) + { + CResourceManager::RemoveLocation(modPath); + } + m_mods.clear(); } bool CPathManager::ModLoaded(const std::string& modPath) { - return CResourceManager::LocationExists(modPath); + return std::find(m_mods.cbegin(), m_mods.cend(), modPath) != m_mods.end(); } std::vector CPathManager::FindMods() const diff --git a/src/app/pathman.h b/src/app/pathman.h index 7726786d..2fedfee6 100644 --- a/src/app/pathman.h +++ b/src/app/pathman.h @@ -40,6 +40,7 @@ public: void AddModSearchDir(const std::string &modSearchDirPath); void AddMod(const std::string &modPath); void RemoveMod(const std::string &modPath); + void RemoveAllMods(); bool ModLoaded(const std::string& modPath); std::vector FindMods() const; @@ -65,4 +66,6 @@ private: std::string m_savePath; //! Mod search paths std::vector m_modSearchDirs; + //! Mod paths + std::vector m_mods; }; diff --git a/src/ui/screen/screen_setup_mods.cpp b/src/ui/screen/screen_setup_mods.cpp index 6fae8151..69cd13fe 100644 --- a/src/ui/screen/screen_setup_mods.cpp +++ b/src/ui/screen/screen_setup_mods.cpp @@ -71,6 +71,8 @@ void CScreenSetupMods::CreateInterface() Math::Point pos, ddim; std::string name; + m_modManager->FindMods(); + CScreenSetup::CreateInterface(); pw = static_cast(m_interface->SearchControl(EVENT_WINDOW5)); if ( pw == nullptr ) return; @@ -155,7 +157,9 @@ bool CScreenSetupMods::EventProcess(const Event &event) modName = pl->GetItemName(pl->GetSelect()); m_modManager->EnableMod(modName); - m_modManager->ReloadMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); + m_modManager->ReloadResources(); m_main->ChangePhase(PHASE_SETUPm); break; @@ -166,7 +170,9 @@ bool CScreenSetupMods::EventProcess(const Event &event) modName = pl->GetItemName(pl->GetSelect()); m_modManager->DisableMod(modName); - m_modManager->ReloadMods(); + m_modManager->SaveMods(); + m_modManager->UpdatePaths(); + m_modManager->ReloadResources(); m_main->ChangePhase(PHASE_SETUPm); break;