소스 검색

Initial support for mod presets system

Ivan Savenko 1 년 전
부모
커밋
c57120f0dd

+ 2 - 0
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

+ 14 - 37
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<ModSettingsStorage> 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())
-	{
-		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())
+	bool modActive = modSettings->isModActive(modname);
+
+	if(modActive)
 	{
-		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

+ 6 - 4
launcher/modManager/cmodlist.h

@@ -13,6 +13,8 @@
 #include <QVariant>
 #include <QVector>
 
+#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<QVariantMap> repositories;
 	QVariantMap localModList;
-	QVariantMap modSettings;
+	std::shared_ptr<ModSettingsStorage> modSettings;
 
 	mutable QMap<QString, CModEntry> 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<ModSettingsStorage> data);
 	virtual void modChanged(QString modID);
 
 	// returns mod by name. Note: mod MUST exist

+ 2 - 0
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);

+ 3 - 32
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<ModSettingsStorage>();
+	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;
 }
 

+ 2 - 3
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<ModSettingsStorage> modSettings;
 	QVariantMap localMods;
 
 	QStringList recentErrors;

+ 123 - 0
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();
+}

+ 37 - 0
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 <QVariantMap>
+
+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;
+};
+

+ 26 - 7
lib/modding/CModHandler.cpp

@@ -238,13 +238,13 @@ std::vector<std::string> 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<TModID> & 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<TModID> & 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<TModID> modsToActivate = modsToActivateJson.convertTo<std::vector<TModID>>();
 
-	coreMod = std::make_unique<CModInfo>(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<CModInfo>(ModScope::scopeBuiltin(), modConfig[ModScope::scopeBuiltin()], JsonNode(JsonPath::builtin("config/gameConfig.json")), true);
 }
 
 std::vector<std::string> CModHandler::getAllMods() const

+ 3 - 3
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 <TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const;
+	std::vector<TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const;
 
 	std::vector<std::string> 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<TModID> & modsToActivate, bool enableMods);
+	void loadOneMod(std::string modName, const std::string & parent, const JsonNode & modSettings, const std::vector<TModID> & modsToActivate, bool enableMods);
 	void loadTranslation(const TModID & modName);
 
 	CModVersion getModVersion(TModID modName) const;

+ 2 - 4
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));
 	}

+ 1 - 1
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);