1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

Initial support for mod presets system

This commit is contained in:
Ivan Savenko 2024-10-16 12:52:08 +00:00
parent 7fdddee503
commit c57120f0dd
12 changed files with 221 additions and 91 deletions

View File

@ -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

View File

@ -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())
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

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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;

View File

@ -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();
}

View File

@ -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;
};

View File

@ -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

View File

@ -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;

View File

@ -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));
}

View File

@ -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);