diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index a56893838..6e407d2f9 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -13,6 +13,7 @@ set(launcher_SRCS modManager/cmodmanager.cpp modManager/imageviewer_moc.cpp modManager/chroniclesextractor.cpp + modManager/modsettingsstorage.cpp settingsView/csettingsview_moc.cpp firstLaunch/firstlaunch_moc.cpp main.cpp @@ -43,6 +44,7 @@ set(launcher_HEADERS modManager/cmodmanager.h modManager/imageviewer_moc.h modManager/chroniclesextractor.h + modManager/modsettingsstorage.h settingsView/csettingsview_moc.h firstLaunch/firstlaunch_moc.h mainwindow_moc.h diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index fad621601..865960a8d 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -33,8 +33,8 @@ QString CModEntry::sizeToString(double size) return QCoreApplication::translate("File size", sizes[index]).arg(QString::number(size, 'f', 1)); } -CModEntry::CModEntry(QVariantMap repository, QVariantMap localData, QVariantMap modSettings, QString modname) - : repository(repository), localData(localData), modSettings(modSettings), modname(modname) +CModEntry::CModEntry(QVariantMap repository, QVariantMap localData, bool modActive, QString modname) + : repository(repository), localData(localData), modActive(modActive), modname(modname) { } @@ -46,7 +46,7 @@ bool CModEntry::isEnabled() const if (!isVisible()) return false; - return modSettings["active"].toBool(); + return modActive; } bool CModEntry::isDisabled() const @@ -250,9 +250,9 @@ void CModList::setLocalModList(QVariantMap data) cachedMods.clear(); } -void CModList::setModSettings(QVariant data) +void CModList::setModSettings(std::shared_ptr data) { - modSettings = data.toMap(); + modSettings = data; cachedMods.clear(); } @@ -294,46 +294,23 @@ CModEntry CModList::getModUncached(QString modname) const { QVariantMap repo; QVariantMap local = localModList[modname].toMap(); - QVariantMap settings; QString path = modname; path = "/" + path.replace(".", "/mods/"); - QVariant conf = getValue(modSettings, path); - if(conf.isNull()) + bool modActive = modSettings->isModActive(modname); + + if(modActive) { - settings["active"] = !local.value("keepDisabled").toBool(); - } - else - { - if(!conf.toMap().isEmpty()) - { - settings = conf.toMap(); - if(settings.value("active").isNull()) - settings["active"] = !local.value("keepDisabled").toBool(); - } - else - settings.insert("active", conf); - } - - if(settings["active"].toBool()) - { - QString rootPath = path.section('/', 0, 1); - if(path != rootPath) - { - conf = getValue(modSettings, rootPath); - const auto confMap = conf.toMap(); - if(!conf.isNull() && !confMap["active"].isNull() && !confMap["active"].toBool()) - { - settings = confMap; - } - } + QString rootModName = modname.section('.', 0, 1); + if (!modSettings->isModActive(rootModName)) + modActive = false; // parent mod is inactive -> submod is also inactive } - if(settings.value("active").toBool()) + if(modActive) { if(!::isCompatible(local.value("compatibility").toMap())) - settings["active"] = false; + modActive = false; // mod not compatible with our version of vcmi -> inactive } for(auto entry : repositories) @@ -366,7 +343,7 @@ CModEntry CModList::getModUncached(QString modname) const } } - return CModEntry(repo, local, settings, modname); + return CModEntry(repo, local, modActive, modname); } bool CModList::hasMod(QString modname) const diff --git a/launcher/modManager/cmodlist.h b/launcher/modManager/cmodlist.h index a9c595a08..ccfe253b0 100644 --- a/launcher/modManager/cmodlist.h +++ b/launcher/modManager/cmodlist.h @@ -13,6 +13,8 @@ #include #include +#include "modsettingsstorage.h" + namespace ModStatus { enum EModStatus @@ -30,13 +32,13 @@ class CModEntry // repository contains newest version only (if multiple are available) QVariantMap repository; QVariantMap localData; - QVariantMap modSettings; + bool modActive; QString modname; QVariant getValueImpl(QString value, bool localized) const; public: - CModEntry(QVariantMap repository, QVariantMap localData, QVariantMap modSettings, QString modname); + CModEntry(QVariantMap repository, QVariantMap localData, bool modActive, QString modname); // installed and enabled bool isEnabled() const; @@ -80,7 +82,7 @@ class CModList { QVector repositories; QVariantMap localModList; - QVariantMap modSettings; + std::shared_ptr modSettings; mutable QMap cachedMods; @@ -92,7 +94,7 @@ public: virtual void reloadRepositories(); virtual void addRepository(QVariantMap data); virtual void setLocalModList(QVariantMap data); - virtual void setModSettings(QVariant data); + virtual void setModSettings(std::shared_ptr data); virtual void modChanged(QString modID); // returns mod by name. Note: mod MUST exist diff --git a/launcher/modManager/cmodlistmodel_moc.cpp b/launcher/modManager/cmodlistmodel_moc.cpp index 258ffbd83..bc24e10d0 100644 --- a/launcher/modManager/cmodlistmodel_moc.cpp +++ b/launcher/modManager/cmodlistmodel_moc.cpp @@ -192,6 +192,8 @@ void CModListModel::resetRepositories() void CModListModel::modChanged(QString modID) { + CModList::modChanged(modID); + int index = modNameToID.indexOf(modID); QModelIndex parent = this->parent(createIndex(0, 0, index)); int row = modIndex[modIndexToName(parent)].indexOf(modID); diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 4288f4a15..c8ae3dfc8 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/launcher/modManager/cmodmanager.cpp @@ -69,15 +69,10 @@ CModManager::CModManager(CModList * modList) loadModSettings(); } -QString CModManager::settingsPath() -{ - return pathToQString(VCMIDirs::get().userConfigPath() / "modSettings.json"); -} - void CModManager::loadModSettings() { - modSettings = JsonUtils::JsonFromFile(settingsPath()).toMap(); - modList->setModSettings(modSettings["activeMods"]); + modSettings = std::make_shared(); + modList->setModSettings(modSettings); } void CModManager::resetRepositories() @@ -248,35 +243,11 @@ bool CModManager::canDisableMod(QString modname) return true; } -static QVariant writeValue(QString path, QVariantMap input, QVariant value) -{ - if(path.size() > 1) - { - - QString entryName = path.section('/', 0, 1); - QString remainder = "/" + path.section('/', 2, -1); - - entryName.remove(0, 1); - input.insert(entryName, writeValue(remainder, input.value(entryName).toMap(), value)); - return input; - } - else - { - return value; - } -} - bool CModManager::doEnableMod(QString mod, bool on) { - QString path = mod; - path = "/activeMods/" + path.replace(".", "/mods/") + "/active"; - - modSettings = writeValue(path, modSettings, QVariant(on)).toMap(); - modList->setModSettings(modSettings["activeMods"]); + modSettings->setModActive(mod, on); modList->modChanged(mod); - JsonUtils::JsonToFile(settingsPath(), modSettings); - return true; } diff --git a/launcher/modManager/cmodmanager.h b/launcher/modManager/cmodmanager.h index 987d1a580..a9b4f5b3e 100644 --- a/launcher/modManager/cmodmanager.h +++ b/launcher/modManager/cmodmanager.h @@ -10,6 +10,7 @@ #pragma once #include "cmodlist.h" +#include "modsettingsstorage.h" class CModManager : public QObject { @@ -17,14 +18,12 @@ class CModManager : public QObject CModList * modList; - QString settingsPath(); - // check-free version of public method bool doEnableMod(QString mod, bool on); bool doInstallMod(QString mod, QString archivePath); bool doUninstallMod(QString mod); - QVariantMap modSettings; + std::shared_ptr modSettings; QVariantMap localMods; QStringList recentErrors; diff --git a/launcher/modManager/modsettingsstorage.cpp b/launcher/modManager/modsettingsstorage.cpp new file mode 100644 index 000000000..6d1e6f9f3 --- /dev/null +++ b/launcher/modManager/modsettingsstorage.cpp @@ -0,0 +1,123 @@ +/* + * modsettignsstorage.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "modsettingsstorage.h" + +#include "../../lib/VCMIDirs.h" +#include "../vcmiqt/jsonutils.h" + +static QVariant writeValue(QString path, QVariantMap input, QVariant value) +{ + if(path.size() > 1) + { + QString entryName = path.section('/', 0, 1); + QString remainder = "/" + path.section('/', 2, -1); + entryName.remove(0, 1); + input.insert(entryName, writeValue(remainder, input.value(entryName).toMap(), value)); + return input; + } + else + { + return value; + } +} + +ModSettingsStorage::ModSettingsStorage() +{ + config = JsonUtils::JsonFromFile(settingsPath()).toMap(); + + // TODO: import from 1.5 format +} + +QString ModSettingsStorage::settingsPath() const +{ + return pathToQString(VCMIDirs::get().userConfigPath() / "modSettings.json"); +} + +void ModSettingsStorage::setRootModActive(const QString & modName, bool on) +{ + QString presetName = getActivePreset(); + QStringList activeMods = getActiveMods(); + + assert(modName.count('.') == 0); // this method should never be used for submods + + if (on) + activeMods.push_back(modName); + else + activeMods.removeAll(modName); + + config = writeValue("/presets/" + presetName + "/mods", config, activeMods).toMap(); + + JsonUtils::JsonToFile(settingsPath(), config); +} + +void ModSettingsStorage::setModSettingActive(const QString & modName, bool on) +{ + QString presetName = getActivePreset(); + QString rootModName = modName.section('.', 0, 0); + QString settingName = modName.section('.', 1); + QVariantMap modSettings = getModSettings(rootModName); + + assert(modName.count('.') != 0); // this method should only be used for submods + + modSettings.insert(settingName, QVariant(on)); + + config = writeValue("/presets/" + presetName + "/settings/" + rootModName, config, modSettings).toMap(); + + JsonUtils::JsonToFile(settingsPath(), config); +} + +void ModSettingsStorage::setModActive(const QString & modName, bool on) +{ + if (modName.contains('.')) + setModSettingActive(modName, on); + else + setRootModActive(modName, on); +} + +void ModSettingsStorage::setActivePreset(const QString & presetName) +{ + config.insert("activePreset", QVariant(presetName)); +} + +QString ModSettingsStorage::getActivePreset() const +{ + return config["activePreset"].toString(); +} + +bool ModSettingsStorage::isModActive(const QString & modName) const +{ + if (modName.contains('.')) + { + QString rootModName = modName.section('.', 0, 0); + QString settingName = modName.section('.', 1); + + return getModSettings(rootModName)[settingName].toBool(); + } + else + return getActiveMods().contains(modName); +} + +QStringList ModSettingsStorage::getActiveMods() const +{ + return getActivePresetData()["mods"].toStringList(); +} + +QVariantMap ModSettingsStorage::getActivePresetData() const +{ + QString presetName = getActivePreset(); + return config["presets"].toMap()[presetName].toMap(); +} + +QVariantMap ModSettingsStorage::getModSettings(const QString & modName) const +{ + QString presetName = getActivePreset(); + return getActivePresetData()["settings"].toMap()[modName].toMap(); +} diff --git a/launcher/modManager/modsettingsstorage.h b/launcher/modManager/modsettingsstorage.h new file mode 100644 index 000000000..76b793d88 --- /dev/null +++ b/launcher/modManager/modsettingsstorage.h @@ -0,0 +1,37 @@ +/* + * modsettignsstorage.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include + +class ModSettingsStorage +{ + QVariantMap config; + + QString settingsPath() const; + + void setRootModActive(const QString & modName, bool on); + void setModSettingActive(const QString & modName, bool on); + + QVariantMap getActivePresetData() const; + QStringList getActiveMods() const; + QVariantMap getModSettings(const QString & modName) const; + +public: + ModSettingsStorage(); + + void setModActive(const QString & modName, bool on); + void setActivePreset(const QString & presetName); + + QString getActivePreset() const; + bool isModActive(const QString & modName) const; + //QStringList getPresetsList() const; +}; + diff --git a/lib/modding/CModHandler.cpp b/lib/modding/CModHandler.cpp index ab1f375ec..ed98a37bb 100644 --- a/lib/modding/CModHandler.cpp +++ b/lib/modding/CModHandler.cpp @@ -238,13 +238,13 @@ std::vector CModHandler::getModList(const std::string & path) const -void CModHandler::loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, bool enableMods) +void CModHandler::loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, const std::vector & modsToActivate, bool enableMods) { for(const std::string & modName : getModList(path)) - loadOneMod(modName, parent, modSettings, enableMods); + loadOneMod(modName, parent, modSettings, modsToActivate, enableMods); } -void CModHandler::loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods) +void CModHandler::loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, const std::vector & modsToActivate, bool enableMods) { boost::to_lower(modName); std::string modFullName = parent.empty() ? modName : parent + '.' + modName; @@ -257,7 +257,8 @@ void CModHandler::loadOneMod(std::string modName, const std::string & parent, co if(CResourceHandler::get("initial")->existsResource(CModInfo::getModFile(modFullName))) { - CModInfo mod(modFullName, modSettings[modName], JsonNode(CModInfo::getModFile(modFullName))); + bool thisModActive = vstd::contains(modsToActivate, modFullName); + CModInfo mod(modFullName, modSettings[modName], JsonNode(CModInfo::getModFile(modFullName)), thisModActive); if (!parent.empty()) // this is submod, add parent to dependencies mod.dependencies.insert(parent); @@ -265,7 +266,7 @@ void CModHandler::loadOneMod(std::string modName, const std::string & parent, co if (mod.isEnabled() && enableMods) activeMods.push_back(modFullName); - loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.isEnabled()); + loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], modsToActivate, enableMods && mod.isEnabled()); } } @@ -274,9 +275,27 @@ void CModHandler::loadMods() JsonNode modConfig; modConfig = loadModSettings(JsonPath::builtin("config/modSettings.json")); - loadMods("", "", modConfig["activeMods"], true); + const JsonNode & modSettings = modConfig["activeMods"]; + const std::string & currentPresetName = modConfig["activePreset"].String(); + const JsonNode & currentPreset = modConfig["presets"][currentPresetName]; + const JsonNode & modsToActivateJson = currentPreset["mods"]; + std::vector modsToActivate = modsToActivateJson.convertTo>(); - coreMod = std::make_unique(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json"))); + for(const auto & settings : currentPreset["settings"].Struct()) + { + if (!vstd::contains(modsToActivate, settings.first)) + continue; // settings for inactive mod + + for (const auto & submod : settings.second.Struct()) + { + if (submod.second.Bool()) + modsToActivate.push_back(settings.first + '.' + submod.first); + } + } + + loadMods("", "", modSettings, modsToActivate, true); + + coreMod = std::make_unique(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json")), true); } std::vector CModHandler::getAllMods() const diff --git a/lib/modding/CModHandler.h b/lib/modding/CModHandler.h index e50a70f9f..ccc4bc540 100644 --- a/lib/modding/CModHandler.h +++ b/lib/modding/CModHandler.h @@ -42,11 +42,11 @@ class DLL_LINKAGE CModHandler final : boost::noncopyable * @param modsToResolve list of valid mod IDs (checkDependencies returned true - TODO: Clarify it.) * @return a vector of the topologically sorted resolved mods: child nodes (dependent mods) have greater index than parents */ - std::vector validateAndSortDependencies(std::vector modsToResolve) const; + std::vector validateAndSortDependencies(std::vector modsToResolve) const; std::vector getModList(const std::string & path) const; - void loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, bool enableMods); - void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, bool enableMods); + void loadMods(const std::string & path, const std::string & parent, const JsonNode & modSettings, const std::vector & modsToActivate, bool enableMods); + void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, const std::vector & modsToActivate, bool enableMods); void loadTranslation(const TModID & modName); CModVersion getModVersion(TModID modName) const; diff --git a/lib/modding/CModInfo.cpp b/lib/modding/CModInfo.cpp index fb0778f6f..aa39b4e7d 100644 --- a/lib/modding/CModInfo.cpp +++ b/lib/modding/CModInfo.cpp @@ -40,12 +40,12 @@ CModInfo::CModInfo(): } -CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config): +CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config, bool isActive): identifier(identifier), dependencies(readModList(config["depends"])), softDependencies(readModList(config["softDepends"])), conflicts(readModList(config["conflicts"])), - explicitlyEnabled(false), + explicitlyEnabled(isActive), implicitlyEnabled(true), validation(PENDING), config(addMeta(config, identifier)) @@ -110,11 +110,9 @@ void CModInfo::loadLocalData(const JsonNode & data) { bool validated = false; implicitlyEnabled = true; - explicitlyEnabled = !config["keepDisabled"].Bool(); verificationInfo.checksum = 0; if (data.isStruct()) { - explicitlyEnabled = data["active"].Bool(); validated = data["validated"].Bool(); updateChecksum(strtol(data["checksum"].String().c_str(), nullptr, 16)); } diff --git a/lib/modding/CModInfo.h b/lib/modding/CModInfo.h index d5c077167..fd0f8319e 100644 --- a/lib/modding/CModInfo.h +++ b/lib/modding/CModInfo.h @@ -55,7 +55,7 @@ public: JsonNode config; CModInfo(); - CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config); + CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config, bool isActive); JsonNode saveLocalData() const; void updateChecksum(ui32 newChecksum);