1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-08 22:26:51 +02:00

Reworked mod handling in Launcher in order to unify code with lib

This commit is contained in:
Ivan Savenko
2024-11-12 20:00:21 +00:00
parent 4945370fe3
commit f8724b9558
24 changed files with 746 additions and 1127 deletions

View File

@@ -7,13 +7,13 @@ set(launcher_SRCS
StdInc.cpp
aboutProject/aboutproject_moc.cpp
modManager/cdownloadmanager_moc.cpp
modManager/cmodlist.cpp
modManager/cmodlistmodel_moc.cpp
modManager/modstateitemmodel_moc.cpp
modManager/cmodlistview_moc.cpp
modManager/cmodmanager.cpp
modManager/modstatecontroller.cpp
modManager/modstatemodel.cpp
modManager/modstate.cpp
modManager/imageviewer_moc.cpp
modManager/chroniclesextractor.cpp
modManager/modsettingsstorage.cpp
settingsView/csettingsview_moc.cpp
firstLaunch/firstlaunch_moc.cpp
main.cpp
@@ -38,13 +38,13 @@ set(launcher_HEADERS
StdInc.h
aboutProject/aboutproject_moc.h
modManager/cdownloadmanager_moc.h
modManager/cmodlist.h
modManager/cmodlistmodel_moc.h
modManager/modstateitemmodel_moc.h
modManager/cmodlistview_moc.h
modManager/cmodmanager.h
modManager/modstatecontroller.h
modManager/modstatemodel.h
modManager/modstate.h
modManager/imageviewer_moc.h
modManager/chroniclesextractor.h
modManager/modsettingsstorage.h
settingsView/csettingsview_moc.h
firstLaunch/firstlaunch_moc.h
mainwindow_moc.h

View File

@@ -206,10 +206,10 @@ void MainWindow::on_startEditorButton_clicked()
startEditor({});
}
const CModList & MainWindow::getModList() const
{
return ui->modlistView->getModList();
}
//const CModList & MainWindow::getModList() const
//{
// return ui->modlistView->getModList();
//}
CModListView * MainWindow::getModView()
{

View File

@@ -46,7 +46,7 @@ public:
explicit MainWindow(QWidget * parent = nullptr);
~MainWindow() override;
const CModList & getModList() const;
// const CModList & getModList() const;
CModListView * getModView();
void updateTranslation();

View File

@@ -1,417 +0,0 @@
/*
* cmodlist.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 "cmodlist.h"
#include "../lib/CConfigHandler.h"
#include "../../lib/filesystem/CFileInputStream.h"
#include "../../lib/GameConstants.h"
#include "../../lib/modding/CModVersion.h"
QString CModEntry::sizeToString(double size)
{
static const std::array sizes {
QT_TRANSLATE_NOOP("File size", "%1 B"),
QT_TRANSLATE_NOOP("File size", "%1 KiB"),
QT_TRANSLATE_NOOP("File size", "%1 MiB"),
QT_TRANSLATE_NOOP("File size", "%1 GiB"),
QT_TRANSLATE_NOOP("File size", "%1 TiB")
};
size_t index = 0;
while(size > 1024 && index < sizes.size())
{
size /= 1024;
index++;
}
return QCoreApplication::translate("File size", sizes[index]).arg(QString::number(size, 'f', 1));
}
CModEntry::CModEntry(QVariantMap repository, QVariantMap localData, bool modActive, QString modname)
: repository(repository), localData(localData), modActive(modActive), modname(modname)
{
}
bool CModEntry::isEnabled() const
{
if(!isInstalled())
return false;
if (!isVisible())
return false;
return modActive;
}
bool CModEntry::isDisabled() const
{
if(!isInstalled())
return false;
return !isEnabled();
}
bool CModEntry::isAvailable() const
{
if(isInstalled())
return false;
return !repository.isEmpty();
}
bool CModEntry::isUpdateable() const
{
if(!isInstalled())
return false;
auto installedVer = localData["installedVersion"].toString().toStdString();
auto availableVer = repository["latestVersion"].toString().toStdString();
return (CModVersion::fromString(installedVer) < CModVersion::fromString(availableVer));
}
bool isCompatible(const QVariantMap & compatibility)
{
auto compatibleMin = CModVersion::fromString(compatibility["min"].toString().toStdString());
auto compatibleMax = CModVersion::fromString(compatibility["max"].toString().toStdString());
return (compatibleMin.isNull() || CModVersion::GameVersion().compatible(compatibleMin, true, true))
&& (compatibleMax.isNull() || compatibleMax.compatible(CModVersion::GameVersion(), true, true));
}
bool CModEntry::isCompatible() const
{
return ::isCompatible(localData["compatibility"].toMap());
}
bool CModEntry::isEssential() const
{
return getName() == "vcmi";
}
bool CModEntry::isInstalled() const
{
return !localData.isEmpty();
}
bool CModEntry::isVisible() const
{
if (isCompatibilityPatch())
{
if (isSubmod())
return false;
}
if (isTranslation())
{
// Do not show not installed translation mods to languages other than player language
if (localData.empty() && getBaseValue("language") != QString::fromStdString(settings["general"]["language"].String()) )
return false;
}
return !localData.isEmpty() || (!repository.isEmpty() && !repository.contains("mod"));
}
bool CModEntry::isTranslation() const
{
return getBaseValue("modType").toString() == "Translation";
}
bool CModEntry::isCompatibilityPatch() const
{
return getBaseValue("modType").toString() == "Compatibility";
}
bool CModEntry::isSubmod() const
{
return getName().contains('.');
}
int CModEntry::getModStatus() const
{
int status = 0;
if(isEnabled())
status |= ModStatus::ENABLED;
if(isInstalled())
status |= ModStatus::INSTALLED;
if(isUpdateable())
status |= ModStatus::UPDATEABLE;
return status;
}
QString CModEntry::getName() const
{
return modname;
}
QString CModEntry::getTopParentName() const
{
assert(isSubmod());
return modname.section('.', 0, 0);
}
QVariant CModEntry::getValue(QString value) const
{
return getValueImpl(value, true);
}
QStringList CModEntry::getDependencies() const
{
QStringList result;
for (auto const & entry : getValue("depends").toStringList())
result.push_back(entry.toLower());
return result;
}
QStringList CModEntry::getConflicts() const
{
QStringList result;
for (auto const & entry : getValue("conflicts").toStringList())
result.push_back(entry.toLower());
return result;
}
QVariant CModEntry::getBaseValue(QString value) const
{
return getValueImpl(value, false);
}
QVariant CModEntry::getValueImpl(QString value, bool localized) const
{
QString langValue = QString::fromStdString(settings["general"]["language"].String());
// Priorities
// 1) data from newest version
// 2) data from preferred language
bool useRepositoryData = repository.contains(value);
if(repository.contains(value) && localData.contains(value))
{
// value is present in both repo and locally installed. Select one from latest version
auto installedVer = localData["installedVersion"].toString().toStdString();
auto availableVer = repository["latestVersion"].toString().toStdString();
useRepositoryData = CModVersion::fromString(installedVer) < CModVersion::fromString(availableVer);
}
auto & storage = useRepositoryData ? repository : localData;
if(localized && storage.contains(langValue))
{
auto langStorage = storage[langValue].toMap();
if (langStorage.contains(value))
return langStorage[value];
}
if(storage.contains(value))
return storage[value];
return QVariant();
}
QVariantMap CModList::copyField(QVariantMap data, QString from, QString to) const
{
QVariantMap renamed;
for(auto it = data.begin(); it != data.end(); it++)
{
QVariantMap modConf = it.value().toMap();
modConf.insert(to, modConf.value(from));
renamed.insert(it.key(), modConf);
}
return renamed;
}
void CModList::reloadRepositories()
{
cachedMods.clear();
}
void CModList::resetRepositories()
{
repositories.clear();
cachedMods.clear();
}
void CModList::addRepository(QVariantMap data)
{
for(auto & key : data.keys())
data[key.toLower()] = data.take(key);
repositories.push_back(copyField(data, "version", "latestVersion"));
cachedMods.clear();
}
void CModList::setLocalModList(QVariantMap data)
{
localModList = copyField(data, "version", "installedVersion");
cachedMods.clear();
}
void CModList::setModSettings(std::shared_ptr<ModSettingsStorage> data)
{
modSettings = data;
cachedMods.clear();
}
void CModList::modChanged(QString modID)
{
cachedMods.clear();
}
static QVariant getValue(QVariant input, QString path)
{
if(path.size() > 1)
{
QString entryName = path.section('/', 0, 1);
QString remainder = "/" + path.section('/', 2, -1);
entryName.remove(0, 1);
return getValue(input.toMap().value(entryName), remainder);
}
else
{
return input;
}
}
const CModEntry & CModList::getMod(QString modName) const
{
modName = modName.toLower();
auto it = cachedMods.find(modName);
if (it != cachedMods.end())
return it.value();
auto itNew = cachedMods.insert(modName, getModUncached(modName));
return *itNew;
}
CModEntry CModList::getModUncached(QString modname) const
{
QVariantMap repo;
QVariantMap local = localModList[modname].toMap();
QString path = modname;
path = "/" + path.replace(".", "/mods/");
bool modActive = modSettings->isModActive(modname);
if(modActive)
{
QString rootModName = modname.section('.', 0, 1);
if (!modSettings->isModActive(rootModName))
modActive = false; // parent mod is inactive -> submod is also inactive
}
if(modActive)
{
if(!::isCompatible(local.value("compatibility").toMap()))
modActive = false; // mod not compatible with our version of vcmi -> inactive
}
for(auto entry : repositories)
{
QVariant repoVal = getValue(entry, path);
if(repoVal.isValid())
{
auto repoValMap = repoVal.toMap();
if(::isCompatible(repoValMap["compatibility"].toMap()))
{
if(repo.empty()
|| CModVersion::fromString(repo["version"].toString().toStdString())
< CModVersion::fromString(repoValMap["version"].toString().toStdString()))
{
//take valid download link, screenshots and mod size before assignment
auto download = repo.value("download");
auto screenshots = repo.value("screenshots");
auto size = repo.value("downloadSize");
repo = repoValMap;
if(repo.value("download").isNull())
{
repo["download"] = download;
if(repo.value("screenshots").isNull()) //taking screenshot from the downloadable version
repo["screenshots"] = screenshots;
}
if(repo.value("downloadSize").isNull())
repo["downloadSize"] = size;
}
}
}
}
return CModEntry(repo, local, modActive, modname);
}
bool CModList::hasMod(QString modname) const
{
if(localModList.contains(modname))
return true;
for(auto entry : repositories)
if(entry.contains(modname))
return true;
return false;
}
QStringList CModList::getRequirements(QString modname)
{
QStringList ret;
if(hasMod(modname))
{
auto mod = getMod(modname);
for(auto entry : mod.getDependencies())
ret += getRequirements(entry.toLower());
}
ret += modname;
return ret;
}
QVector<QString> CModList::getModList() const
{
QSet<QString> knownMods;
QVector<QString> modList;
for(auto repo : repositories)
{
for(auto it = repo.begin(); it != repo.end(); it++)
{
knownMods.insert(it.key().toLower());
}
}
for(auto it = localModList.begin(); it != localModList.end(); it++)
{
knownMods.insert(it.key().toLower());
}
for(auto entry : knownMods)
{
modList.push_back(entry);
}
return modList;
}
QVector<QString> CModList::getChildren(QString parent) const
{
QVector<QString> children;
int depth = parent.count('.') + 1;
for(const QString & mod : getModList())
{
if(mod.count('.') == depth && mod.startsWith(parent))
children.push_back(mod);
}
return children;
}

View File

@@ -1,118 +0,0 @@
/*
* cmodlist.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>
#include <QVariant>
#include <QVector>
#include "modsettingsstorage.h"
namespace ModStatus
{
enum EModStatus
{
MASK_NONE = 0,
ENABLED = 1,
INSTALLED = 2,
UPDATEABLE = 4,
MASK_ALL = 255
};
}
class CModEntry
{
// repository contains newest version only (if multiple are available)
QVariantMap repository;
QVariantMap localData;
bool modActive;
QString modname;
QVariant getValueImpl(QString value, bool localized) const;
public:
CModEntry(QVariantMap repository, QVariantMap localData, bool modActive, QString modname);
// installed and enabled
bool isEnabled() const;
// installed but disabled
bool isDisabled() const;
// available in any of repositories but not installed
bool isAvailable() const;
// installed and greater version exists in repository
bool isUpdateable() const;
// installed
bool isInstalled() const;
// vcmi essential files
bool isEssential() const;
// checks if version is compatible with vcmi
bool isCompatible() const;
// returns true if mod should be visible in Launcher
bool isVisible() const;
// returns true if mod type is Translation
bool isTranslation() const;
// returns true if mod type is Compatibility
bool isCompatibilityPatch() const;
// returns true if this is a submod
bool isSubmod() const;
// see ModStatus enum
int getModStatus() const;
// Returns mod name / identifier (not human-readable)
QString getName() const;
// For submods only. Returns mod name / identifier of a top-level parent mod
QString getTopParentName() const;
// get value of some field in mod structure. Returns empty optional if value is not present
QVariant getValue(QString value) const;
QVariant getBaseValue(QString value) const;
QStringList getDependencies() const;
QStringList getConflicts() const;
static QString sizeToString(double size);
};
class CModList
{
QVector<QVariantMap> repositories;
QVariantMap localModList;
std::shared_ptr<ModSettingsStorage> modSettings;
mutable QMap<QString, CModEntry> cachedMods;
QVariantMap copyField(QVariantMap data, QString from, QString to) const;
CModEntry getModUncached(QString modname) const;
public:
virtual void resetRepositories();
virtual void reloadRepositories();
virtual void addRepository(QVariantMap data);
virtual void setLocalModList(QVariantMap data);
virtual void setModSettings(std::shared_ptr<ModSettingsStorage> data);
virtual void modChanged(QString modID);
// returns mod by name. Note: mod MUST exist
const CModEntry & getMod(QString modname) const;
// returns list of all mods necessary to run selected one, including mod itself
// order is: first mods in list don't have any dependencies, last mod is modname
// note: may include mods not present in list
QStringList getRequirements(QString modname);
bool hasMod(QString modname) const;
// returns list of all available mods
QVector<QString> getModList() const;
QVector<QString> getChildren(QString parent) const;
};

View File

@@ -17,8 +17,9 @@
#include <QCryptographicHash>
#include <QRegularExpression>
#include "cmodlistmodel_moc.h"
#include "cmodmanager.h"
#include "modstatemodel.h"
#include "modstateitemmodel_moc.h"
#include "modstatecontroller.h"
#include "cdownloadmanager_moc.h"
#include "chroniclesextractor.h"
#include "../settingsView/csettingsview_moc.h"
@@ -34,15 +35,11 @@
#include <future>
static double mbToBytes(double mb)
{
return mb * 1024 * 1024;
}
void CModListView::setupModModel()
{
modModel = new CModListModel(this);
manager = std::make_unique<CModManager>(modModel);
modStateModel = std::make_shared<ModStateModel>();
modModel = new ModStateItemModel(this);
manager = std::make_unique<ModStateController>(modStateModel);
}
void CModListView::changeEvent(QEvent *event)
@@ -148,13 +145,7 @@ CModListView::CModListView(QWidget * parent)
dlManager = nullptr;
if(settings["launcher"]["autoCheckRepositories"].Bool())
{
loadRepositories();
}
else
{
manager->resetRepositories();
}
#ifdef VCMI_MOBILE
for(auto * scrollWidget : {
@@ -171,8 +162,6 @@ CModListView::CModListView(QWidget * parent)
void CModListView::loadRepositories()
{
manager->resetRepositories();
QStringList repositories;
if (settings["launcher"]["defaultRepositoryEnabled"].Bool())
@@ -223,7 +212,7 @@ static QString replaceIfNotEmpty(QStringList value, QString pattern)
return "";
}
QString CModListView::genChangelogText(CModEntry & mod)
QString CModListView::genChangelogText(ModState & mod)
{
QString headerTemplate = "<p><span style=\" font-weight:600;\">%1: </span></p>";
QString entryBegin = "<p align=\"justify\"><ul>";
@@ -233,7 +222,7 @@ QString CModListView::genChangelogText(CModEntry & mod)
QString result;
QVariantMap changelog = mod.getValue("changelog").toMap();
QMap<QString, QStringList> changelog = mod.getChangelog();
QList<QString> versions = changelog.keys();
std::sort(versions.begin(), versions.end(), [](QString lesser, QString greater)
@@ -246,7 +235,7 @@ QString CModListView::genChangelogText(CModEntry & mod)
{
result += headerTemplate.arg(version);
result += entryBegin;
for(auto & line : changelog.value(version).toStringList())
for(auto & line : changelog.value(version))
result += entryLine.arg(line);
result += entryEnd;
}
@@ -259,21 +248,21 @@ QStringList CModListView::getModNames(QStringList input)
for(const auto & modID : input)
{
auto mod = modModel->getMod(modID.toLower());
auto mod = modStateModel->getMod(modID);
QString displayName = mod.getValue("name").toString();
QString displayName = mod.getName();
if (displayName.isEmpty())
displayName = modID.toLower();
if (mod.isSubmod())
{
auto parentModID = mod.getTopParentName();
auto parentMod = modModel->getMod(parentModID.toLower());
QString parentDisplayName = parentMod.getValue("name").toString();
auto parentModID = mod.getTopParentID();
auto parentMod = modStateModel->getMod(parentModID);
QString parentDisplayName = parentMod.getName();
if (parentDisplayName.isEmpty())
parentDisplayName = parentModID.toLower();
if (mod.isInstalled())
if (modStateModel->isModInstalled(modID))
displayName = QString("%1 (%2)").arg(displayName, parentDisplayName);
else
displayName = parentDisplayName;
@@ -283,7 +272,7 @@ QStringList CModListView::getModNames(QStringList input)
return result;
}
QString CModListView::genModInfoText(CModEntry & mod)
QString CModListView::genModInfoText(ModState & mod)
{
QString prefix = "<p><span style=\" font-weight:600;\">%1: </span>"; // shared prefix
QString redPrefix = "<p><span style=\" font-weight:600; color:red\">%1: </span>"; // shared prefix
@@ -297,29 +286,30 @@ QString CModListView::genModInfoText(CModEntry & mod)
QString result;
result += replaceIfNotEmpty(mod.getValue("name"), lineTemplate.arg(tr("Mod name")));
result += replaceIfNotEmpty(mod.getValue("installedVersion"), lineTemplate.arg(tr("Installed version")));
result += replaceIfNotEmpty(mod.getValue("latestVersion"), lineTemplate.arg(tr("Latest version")));
result += replaceIfNotEmpty(mod.getName(), lineTemplate.arg(tr("Mod name")));
result += replaceIfNotEmpty(mod.getInstalledVersion(), lineTemplate.arg(tr("Installed version")));
result += replaceIfNotEmpty(mod.getRepositoryVersion(), lineTemplate.arg(tr("Latest version")));
if(mod.getValue("localSizeBytes").isValid())
result += replaceIfNotEmpty(CModEntry::sizeToString(mod.getValue("localSizeBytes").toDouble()), lineTemplate.arg(tr("Size")));
if((mod.isAvailable() || mod.isUpdateable()) && mod.getValue("downloadSize").isValid())
result += replaceIfNotEmpty(CModEntry::sizeToString(mbToBytes(mod.getValue("downloadSize").toDouble())), lineTemplate.arg(tr("Download size")));
if(!mod.getLocalSizeFormatted().isEmpty())
result += replaceIfNotEmpty(mod.getLocalSizeFormatted(), lineTemplate.arg(tr("Size")));
if((!mod.isInstalled() || mod.isUpdateAvailable()) && !mod.getDownloadSizeFormatted().isEmpty())
result += replaceIfNotEmpty(mod.getDownloadSizeFormatted(), lineTemplate.arg(tr("Download size")));
result += replaceIfNotEmpty(mod.getValue("author"), lineTemplate.arg(tr("Authors")));
result += replaceIfNotEmpty(mod.getAuthors(), lineTemplate.arg(tr("Authors")));
if(mod.getValue("licenseURL").isValid())
result += urlTemplate.arg(tr("License")).arg(mod.getValue("licenseURL").toString()).arg(mod.getValue("licenseName").toString());
if(mod.getLicenseName().isEmpty())
result += urlTemplate.arg(tr("License")).arg(mod.getLicenseUrl()).arg(mod.getLicenseName());
if(mod.getValue("contact").isValid())
result += urlTemplate.arg(tr("Contact")).arg(mod.getValue("contact").toString()).arg(mod.getValue("contact").toString());
if(mod.getContact().isEmpty())
result += urlTemplate.arg(tr("Contact")).arg(mod.getContact()).arg(mod.getContact());
//compatibility info
if(!mod.isCompatible())
{
auto compatibilityInfo = mod.getValue("compatibility").toMap();
auto minStr = compatibilityInfo.value("min").toString();
auto maxStr = compatibilityInfo.value("max").toString();
auto compatibilityInfo = mod.getCompatibleVersionRange();
auto minStr = compatibilityInfo.first;
auto maxStr = compatibilityInfo.second;
result += incompatibleString.arg(tr("Compatibility"));
if(minStr == maxStr)
@@ -338,31 +328,24 @@ QString CModListView::genModInfoText(CModEntry & mod)
}
}
QStringList supportedLanguages;
QVariant baseLanguageVariant = mod.getBaseValue("language");
QVariant baseLanguageVariant = mod.getBaseLanguage();
QString baseLanguageID = baseLanguageVariant.isValid() ? baseLanguageVariant.toString() : "english";
bool needToShowSupportedLanguages = false;
QStringList supportedLanguages = mod.getSupportedLanguages();
for(const auto & language : Languages::getLanguageList())
if(supportedLanguages.size() > 1)
{
QString languageID = QString::fromStdString(language.identifier);
QStringList supportedLanguagesTranslated;
if (languageID != baseLanguageID && !mod.getValue(languageID).isValid())
continue;
for (const auto & languageID : supportedLanguages)
supportedLanguagesTranslated += QApplication::translate("Language", Languages::getLanguageOptions(languageID.toStdString()).nameEnglish.c_str());
if (languageID != baseLanguageID)
needToShowSupportedLanguages = true;
supportedLanguages += QApplication::translate("Language", language.nameEnglish.c_str());
result += replaceIfNotEmpty(supportedLanguagesTranslated, lineTemplate.arg(tr("Languages")));
}
if(needToShowSupportedLanguages)
result += replaceIfNotEmpty(supportedLanguages, lineTemplate.arg(tr("Languages")));
result += replaceIfNotEmpty(getModNames(mod.getDependencies()), lineTemplate.arg(tr("Required mods")));
result += replaceIfNotEmpty(getModNames(mod.getConflicts()), lineTemplate.arg(tr("Conflicting mods")));
result += replaceIfNotEmpty(mod.getValue("description"), textTemplate.arg(tr("Description")));
result += replaceIfNotEmpty(mod.getDescription(), textTemplate.arg(tr("Description")));
result += "<p></p>"; // to get some empty space
@@ -413,7 +396,7 @@ void CModListView::selectMod(const QModelIndex & index)
else
{
const auto modName = index.data(ModRoles::ModNameRole).toString();
auto mod = modModel->getMod(modName);
auto mod = modStateModel->getMod(modName);
ui->modInfoBrowser->setHtml(genModInfoText(mod));
ui->changelogBrowser->setHtml(genChangelogText(mod));
@@ -429,15 +412,15 @@ void CModListView::selectMod(const QModelIndex & index)
ui->enableButton->setVisible(mod.isDisabled());
ui->installButton->setVisible(mod.isAvailable() && !mod.isSubmod());
ui->uninstallButton->setVisible(mod.isInstalled() && !mod.isSubmod());
ui->updateButton->setVisible(mod.isUpdateable());
ui->updateButton->setVisible(mod.isUpdateAvailable());
// Block buttons if action is not allowed at this time
// TODO: automate handling of some of these cases instead of forcing player
// to resolve all conflicts manually.
ui->disableButton->setEnabled(!hasDependentMods && !mod.isEssential());
ui->disableButton->setEnabled(!hasDependentMods && !mod.isHidden());
ui->enableButton->setEnabled(!hasBlockingMods && !hasInvalidDeps);
ui->installButton->setEnabled(!hasInvalidDeps);
ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isEssential());
ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isHidden());
ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods);
loadScreenshots();
@@ -471,35 +454,16 @@ void CModListView::on_lineEdit_textChanged(const QString & arg1)
void CModListView::on_comboBox_currentIndexChanged(int index)
{
switch(index)
{
case 0:
filterModel->setTypeFilter(ModStatus::MASK_NONE, ModStatus::MASK_NONE);
break;
case 1:
filterModel->setTypeFilter(ModStatus::MASK_NONE, ModStatus::INSTALLED);
break;
case 2:
filterModel->setTypeFilter(ModStatus::INSTALLED, ModStatus::INSTALLED);
break;
case 3:
filterModel->setTypeFilter(ModStatus::UPDATEABLE, ModStatus::UPDATEABLE);
break;
case 4:
filterModel->setTypeFilter(ModStatus::ENABLED | ModStatus::INSTALLED, ModStatus::ENABLED | ModStatus::INSTALLED);
break;
case 5:
filterModel->setTypeFilter(ModStatus::INSTALLED, ModStatus::ENABLED | ModStatus::INSTALLED);
break;
}
auto enumIndex = static_cast<ModFilterMask>(index);
filterModel->setTypeFilter(enumIndex);
}
QStringList CModListView::findInvalidDependencies(QString mod)
{
QStringList ret;
for(QString requirement : modModel->getRequirements(mod))
for(QString requirement : modStateModel->getMod(mod).getDependencies())
{
if(!modModel->hasMod(requirement) && !modModel->hasMod(requirement.split(QChar('.'))[0]))
if(!modStateModel->isModExists(requirement) && !modStateModel->isModExists(modStateModel->getMod(requirement).getTopParentID()))
ret += requirement;
}
return ret;
@@ -508,11 +472,11 @@ QStringList CModListView::findInvalidDependencies(QString mod)
QStringList CModListView::findBlockingMods(QString modUnderTest)
{
QStringList ret;
auto required = modModel->getRequirements(modUnderTest);
auto required = modStateModel->getMod(modUnderTest).getDependencies();
for(QString name : modModel->getModList())
for(QString name : modStateModel->getAllMods())
{
auto mod = modModel->getMod(name);
auto mod = modStateModel->getMod(name);
if(mod.isEnabled())
{
@@ -531,9 +495,9 @@ QStringList CModListView::findBlockingMods(QString modUnderTest)
QStringList CModListView::findDependentMods(QString mod, bool excludeDisabled)
{
QStringList ret;
for(QString modName : modModel->getModList())
for(QString modName : modStateModel->getAllMods())
{
auto current = modModel->getMod(modName);
auto current = modStateModel->getMod(modName);
if(!current.isInstalled() || !current.isVisible())
continue;
@@ -561,9 +525,11 @@ void CModListView::enableModByName(QString modName)
assert(findBlockingMods(modName).empty());
assert(findInvalidDependencies(modName).empty());
for(auto & name : modModel->getRequirements(modName))
auto mod = modStateModel->getMod(modName);
for(auto & name : mod.getDependencies())
{
if(modModel->getMod(name).isDisabled())
if(modStateModel->getMod(name).isDisabled())
manager->enableMod(name);
}
emit modsChanged();
@@ -580,7 +546,7 @@ void CModListView::on_disableButton_clicked()
void CModListView::disableModByName(QString modName)
{
if(modModel->hasMod(modName) && modModel->getMod(modName).isEnabled())
if(modStateModel->isModExists(modName) && modStateModel->getMod(modName).isEnabled())
manager->disableMod(modName);
emit modsChanged();
@@ -592,12 +558,12 @@ void CModListView::on_updateButton_clicked()
assert(findInvalidDependencies(modName).empty());
for(auto & name : modModel->getRequirements(modName))
for(auto & name : modStateModel->getMod(modName).getDependencies())
{
auto mod = modModel->getMod(name);
auto mod = modStateModel->getMod(name);
// update required mod, install missing (can be new dependency)
if(mod.isUpdateable() || !mod.isInstalled())
downloadFile(name + ".zip", mod.getValue("download").toString(), name, mbToBytes(mod.getValue("downloadSize").toDouble()));
if(mod.isUpdateAvailable() || !mod.isInstalled())
downloadFile(name + ".zip", mod.getDownloadUrl(), name, mod.getDownloadSizeMegabytes());
}
}
@@ -606,9 +572,9 @@ void CModListView::on_uninstallButton_clicked()
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
// NOTE: perhaps add "manually installed" flag and uninstall those dependencies that don't have it?
if(modModel->hasMod(modName) && modModel->getMod(modName).isInstalled())
if(modStateModel->isModExists(modName) && modStateModel->getMod(modName).isInstalled())
{
if(modModel->getMod(modName).isEnabled())
if(modStateModel->getMod(modName).isEnabled())
manager->disableMod(modName);
manager->uninstallMod(modName);
}
@@ -622,19 +588,19 @@ void CModListView::on_installButton_clicked()
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
assert(findInvalidDependencies(modName).empty());
for(auto & name : modModel->getRequirements(modName))
for(auto & name : modStateModel->getMod(modName).getDependencies())
{
auto mod = modModel->getMod(name);
auto mod = modStateModel->getMod(name);
if(mod.isAvailable())
downloadFile(name + ".zip", mod.getValue("download").toString(), name, mbToBytes(mod.getValue("downloadSize").toDouble()));
downloadFile(name + ".zip", mod.getDownloadUrl(), name, mod.getDownloadSizeMegabytes());
else if(!mod.isEnabled())
enableModByName(name);
}
for(auto & name : modModel->getMod(modName).getConflicts())
for(auto & name : modStateModel->getMod(modName).getConflicts())
{
auto mod = modModel->getMod(name);
auto mod = modStateModel->getMod(name);
if(mod.isEnabled())
{
//TODO: consider reverse dependencies disabling
@@ -697,8 +663,8 @@ void CModListView::manualInstallFile(QString filePath)
for(auto widget : qApp->allWidgets())
if(auto settingsView = qobject_cast<CSettingsView *>(widget))
settingsView->loadSettings();
manager->loadMods();
manager->loadModSettings();
// manager->loadMods();
// manager->loadModSettings();
}
}
}
@@ -726,7 +692,7 @@ void CModListView::downloadFile(QString file, QUrl url, QString description, qin
connect(manager.get(), SIGNAL(extractionProgress(qint64,qint64)),
this, SLOT(extractionProgress(qint64,qint64)));
connect(modModel, &CModListModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged);
connect(modModel, &ModStateItemModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged);
const auto progressBarFormat = tr("Downloading %1. %p% (%v MB out of %m MB) finished").arg(description);
ui->progressBar->setFormat(progressBarFormat);
@@ -849,8 +815,8 @@ void CModListView::installFiles(QStringList files)
images.push_back(filename);
}
if (!repositories.empty())
manager->loadRepositories(repositories);
// if (!repositories.empty())
// manager->loadRepositories(repositories);
if(!mods.empty())
installMods(mods);
@@ -881,8 +847,8 @@ void CModListView::installFiles(QStringList files)
{
//update
CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &){ return true; });
manager->loadMods();
modModel->reloadRepositories();
// manager->loadMods();
// modModel->reloadRepositories();
emit modsChanged();
}
}
@@ -909,7 +875,7 @@ void CModListView::installMods(QStringList archives)
// disable mod(s), to properly recalculate dependencies, if changed
for(QString mod : boost::adaptors::reverse(modNames))
{
CModEntry entry = modModel->getMod(mod);
ModState entry = modStateModel->getMod(mod);
if(entry.isInstalled())
{
// enable mod if installed and enabled
@@ -927,7 +893,7 @@ void CModListView::installMods(QStringList archives)
// uninstall old version of mod, if installed
for(QString mod : boost::adaptors::reverse(modNames))
{
if(modModel->getMod(mod).isInstalled())
if(modStateModel->getMod(mod).isInstalled())
manager->uninstallMod(mod);
}
@@ -941,19 +907,19 @@ void CModListView::installMods(QStringList archives)
enableMod = [&](QString modName)
{
auto mod = modModel->getMod(modName);
if(mod.isInstalled() && !mod.getValue("keepDisabled").toBool())
auto mod = modStateModel->getMod(modName);
if(mod.isInstalled() && !mod.isKeptDisabled())
{
for (auto const & dependencyName : mod.getDependencies())
{
auto dependency = modModel->getMod(dependencyName);
auto dependency = modStateModel->getMod(dependencyName);
if(dependency.isDisabled())
manager->enableMod(dependencyName);
}
if(mod.isDisabled() && manager->enableMod(modName))
{
for(QString child : modModel->getChildren(modName))
for(QString child : modStateModel->getSubmods(modName))
enableMod(child);
}
}
@@ -1025,9 +991,9 @@ void CModListView::loadScreenshots()
ui->screenshotsList->clear();
QString modName = ui->allModsView->currentIndex().data(ModRoles::ModNameRole).toString();
assert(modModel->hasMod(modName)); //should be filtered out by check above
assert(modStateModel->isModExists(modName)); //should be filtered out by check above
for(QString url : modModel->getMod(modName).getValue("screenshots").toStringList())
for(QString url : modStateModel->getMod(modName).getScreenshots())
{
// URL must be encoded to something else to get rid of symbols illegal in file names
const auto hashed = QCryptographicHash::hash(url.toUtf8(), QCryptographicHash::Md5);
@@ -1061,52 +1027,51 @@ void CModListView::on_screenshotsList_clicked(const QModelIndex & index)
}
}
const CModList & CModListView::getModList() const
{
assert(modModel);
return *modModel;
}
//const CModList & CModListView::getModList() const
//{
// assert(modModel);
// return *modModel;
//}
void CModListView::doInstallMod(const QString & modName)
{
assert(findInvalidDependencies(modName).empty());
for(auto & name : modModel->getRequirements(modName))
for(auto & name : modStateModel->getMod(modName).getDependencies())
{
auto mod = modModel->getMod(name);
auto mod = modStateModel->getMod(name);
if(!mod.isInstalled())
downloadFile(name + ".zip", mod.getValue("download").toString(), name, mbToBytes(mod.getValue("downloadSize").toDouble()));
downloadFile(name + ".zip", mod.getDownloadUrl(), name, mod.getDownloadSizeMegabytes());
}
}
bool CModListView::isModAvailable(const QString & modName)
{
auto mod = modModel->getMod(modName);
return mod.isAvailable();
return !modStateModel->isModInstalled(modName);
}
bool CModListView::isModEnabled(const QString & modName)
{
auto mod = modModel->getMod(modName);
auto mod = modStateModel->getMod(modName);
return mod.isEnabled();
}
bool CModListView::isModInstalled(const QString & modName)
{
auto mod = modModel->getMod(modName);
auto mod = modStateModel->getMod(modName);
return mod.isInstalled();
}
QString CModListView::getTranslationModName(const QString & language)
{
for(const auto & modName : modModel->getModList())
for(const auto & modName : modStateModel->getAllMods())
{
auto mod = modModel->getMod(modName);
auto mod = modStateModel->getMod(modName);
if (!mod.isTranslation())
continue;
if (mod.getBaseValue("language").toString() != language)
if (mod.getBaseLanguage() != language)
continue;
return modName;
@@ -1121,7 +1086,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
return;
auto modName = index.data(ModRoles::ModNameRole).toString();
auto mod = modModel->getMod(modName);
auto mod = modStateModel->getMod(modName);
bool hasInvalidDeps = !findInvalidDependencies(modName).empty();
bool hasBlockingMods = !findBlockingMods(modName).empty();
@@ -1133,7 +1098,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
return;
}
if(!hasInvalidDeps && !hasDependentMods && mod.isUpdateable() && index.column() == ModFields::STATUS_UPDATE)
if(!hasInvalidDeps && !hasDependentMods && mod.isUpdateAvailable() && index.column() == ModFields::STATUS_UPDATE)
{
on_updateButton_clicked();
return;
@@ -1155,7 +1120,7 @@ void CModListView::on_allModsView_doubleClicked(const QModelIndex &index)
return;
}
if(!hasDependentMods && !mod.isEssential() && mod.isEnabled())
if(!hasDependentMods && !mod.isVisible() && mod.isEnabled())
{
on_disableButton_clicked();
return;

View File

@@ -17,21 +17,23 @@ namespace Ui
class CModListView;
}
class CModManager;
class ModStateController;
class CModList;
class CModListModel;
class ModStateItemModel;
class ModStateModel;
class CModFilterModel;
class CDownloadManager;
class QTableWidgetItem;
class CModEntry;
class ModState;
class CModListView : public QWidget
{
Q_OBJECT
std::unique_ptr<CModManager> manager;
CModListModel * modModel;
std::shared_ptr<ModStateModel> modStateModel;
std::unique_ptr<ModStateController> manager;
ModStateItemModel * modModel;
CModFilterModel * filterModel;
CDownloadManager * dlManager;
@@ -59,8 +61,8 @@ class CModListView : public QWidget
void installMaps(QStringList maps);
void installFiles(QStringList mods);
QString genChangelogText(CModEntry & mod);
QString genModInfoText(CModEntry & mod);
QString genChangelogText(ModState & mod);
QString genModInfoText(ModState & mod);
void changeEvent(QEvent *event) override;
void dragEnterEvent(QDragEnterEvent* event) override;
@@ -79,7 +81,7 @@ public:
void selectMod(const QModelIndex & index);
const CModList & getModList() const;
//const CModList & getModList() const;
// First Launch View interface

View File

@@ -1,191 +0,0 @@
/*
* 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;
}
}
void ModSettingsStorage::createInitialPreset()
{
// TODO: scan mods directory for all its content?
QStringList modList;
QVariantMap preset;
QVariantMap presetList;
modList.push_back("vcmi");
preset.insert("mods", modList);
presetList.insert("default", preset);
config.insert("presets", presetList);
}
void ModSettingsStorage::importInitialPreset()
{
QStringList modList;
QMap<QString, QVariantMap> modSettings;
QVariantMap activeMods = config["activeMods"].toMap();
for (QVariantMap::const_iterator modEntry = activeMods.begin(); modEntry != activeMods.end(); ++modEntry)
{
if (modEntry.value().toMap()["active"].toBool())
modList.push_back(modEntry.key());
QVariantMap submods = modEntry.value().toMap()["mods"].toMap();
for (QVariantMap::const_iterator submodEntry = submods.begin(); submodEntry != submods.end(); ++submodEntry)
modSettings[modEntry.key()].insert(submodEntry.key(), submodEntry.value().toMap()["active"]);
}
QVariantMap importedPreset;
QVariantMap modSettingsVariant;
QVariantMap presetList;
for (QMap<QString, QVariantMap>::const_iterator modEntry = modSettings.begin(); modEntry != modSettings.end(); ++modEntry)
modSettingsVariant.insert(modEntry.key(), modEntry.value());
importedPreset.insert("mods", modList);
importedPreset.insert("settings", modSettingsVariant);
presetList.insert("default", importedPreset);
config.insert("presets", presetList);
}
ModSettingsStorage::ModSettingsStorage()
{
config = JsonUtils::JsonFromFile(settingsPath()).toMap();
if (!config.contains("presets"))
{
config.insert("activePreset", QVariant("default"));
if (config.contains("activeMods"))
importInitialPreset(); // 1.5 format import
else
createInitialPreset(); // new install
JsonUtils::JsonToFile(settingsPath(), config);
}
}
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::registerNewMod(const QString & modName, bool keepDisabled)
{
if (!modName.contains('.'))
return;
QString rootModName = modName.section('.', 0, 0);
QString settingName = modName.section('.', 1);
QVariantMap modSettings = getModSettings(rootModName);
if (modSettings.contains(settingName))
return;
setModSettingActive(modName, !keepDisabled);
}
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

@@ -1,42 +0,0 @@
/*
* 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 createInitialPreset();
void importInitialPreset();
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 registerNewMod(const QString & modName, bool keepDisabled);
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

@@ -0,0 +1,212 @@
/*
* modstate.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 "modstate.h"
#include "../../lib/modding/ModDescription.h"
#include "../../lib/json/JsonNode.h"
#include "../../lib/texts/CGeneralTextHandler.h"
ModState::ModState(const ModDescription & impl)
: impl(impl)
{
}
QString ModState::getName() const
{
return QString::fromStdString(impl.getName());
}
QString ModState::getType() const
{
return QString::fromStdString(impl.getValue("modType").String());
}
QString ModState::getDescription() const
{
return QString::fromStdString(impl.getValue("description").String());
}
QString ModState::getID() const
{
return QString::fromStdString(impl.getID());
}
QString ModState::getParentID() const
{
return QString::fromStdString(impl.getParentID());
}
QString ModState::getTopParentID() const
{
return QString::fromStdString(impl.getParentID());
}
template<typename Container>
QStringList stringListStdToQt(const Container & container)
{
QStringList result;
for (const auto & str : container)
result.push_back(QString::fromStdString(str));
return result;
}
QStringList ModState::getDependencies() const
{
return stringListStdToQt(impl.getDependencies());
}
QStringList ModState::getConflicts() const
{
return stringListStdToQt(impl.getConflicts());
}
QStringList ModState::getScreenshots() const
{
return {}; // TODO
}
QString ModState::getBaseLanguage() const
{
return QString::fromStdString(impl.getBaseLanguage());
}
QStringList ModState::getSupportedLanguages() const
{
return {}; //TODO
}
QMap<QString, QStringList> ModState::getChangelog() const
{
return {}; //TODO
}
QString ModState::getInstalledVersion() const
{
return QString::fromStdString(impl.getLocalValue("version").String());
}
QString ModState::getRepositoryVersion() const
{
return QString::fromStdString(impl.getRepositoryValue("version").String());
}
double ModState::getDownloadSizeMegabytes() const
{
return impl.getRepositoryValue("downloadSize").Float();
}
size_t ModState::getDownloadSizeBytes() const
{
return getDownloadSizeMegabytes() * 1024 * 1024;
}
QString ModState::getDownloadSizeFormatted() const
{
return {}; // TODO
}
QString ModState::getLocalSizeFormatted() const
{
return {}; // TODO
}
QString ModState::getAuthors() const
{
return QString::fromStdString(impl.getValue("authors").String());
}
QString ModState::getContact() const
{
return QString::fromStdString(impl.getValue("contact").String());
}
QString ModState::getLicenseUrl() const
{
return QString::fromStdString(impl.getValue("licenseUrl").String());
}
QString ModState::getLicenseName() const
{
return QString::fromStdString(impl.getValue("licenseName").String());
}
QString ModState::getDownloadUrl() const
{
return QString::fromStdString(impl.getRepositoryValue("download").String());
}
QPair<QString, QString> ModState::getCompatibleVersionRange() const
{
return {}; // TODO
}
bool ModState::isSubmod() const
{
return !getParentID().isEmpty();
}
bool ModState::isCompatibility() const
{
return impl.isCompatibility();
}
bool ModState::isTranslation() const
{
return impl.isTranslation();
}
bool ModState::isVisible() const
{
return !isHidden();
}
bool ModState::isHidden() const
{
if (isTranslation() && !isInstalled())
return impl.getBaseLanguage() == CGeneralTextHandler::getPreferredLanguage();
return isCompatibility() || getID() == "vcmi";
}
bool ModState::isDisabled() const
{
return false; // TODO
}
bool ModState::isEnabled() const
{
return true; // TODO
}
bool ModState::isAvailable() const
{
return !isInstalled();
}
bool ModState::isInstalled() const
{
return impl.isInstalled();
}
bool ModState::isUpdateAvailable() const
{
return getInstalledVersion() != getRepositoryVersion() && !getRepositoryVersion().isEmpty();
}
bool ModState::isCompatible() const
{
return true; //TODO
}
bool ModState::isKeptDisabled() const
{
return impl.keepDisabled();
}

View File

@@ -0,0 +1,71 @@
/*
* modstate.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
VCMI_LIB_NAMESPACE_BEGIN
class ModDescription;
VCMI_LIB_NAMESPACE_END
/// Class that represent current state of mod in Launcher
/// Provides Qt-based interface to library class ModDescription
class ModState
{
const ModDescription & impl;
public:
explicit ModState(const ModDescription & impl);
QString getName() const;
QString getType() const;
QString getDescription() const;
QString getID() const;
QString getParentID() const;
QString getTopParentID() const;
QStringList getDependencies() const;
QStringList getConflicts() const;
QStringList getScreenshots() const;
QString getBaseLanguage() const;
QStringList getSupportedLanguages() const;
QMap<QString, QStringList> getChangelog() const;
QString getInstalledVersion() const;
QString getRepositoryVersion() const;
double getDownloadSizeMegabytes() const;
size_t getDownloadSizeBytes() const;
QString getDownloadSizeFormatted() const;
QString getLocalSizeFormatted() const;
QString getAuthors() const;
QString getContact() const;
QString getLicenseUrl() const;
QString getLicenseName() const;
QString getDownloadUrl() const;
QPair<QString, QString> getCompatibleVersionRange() const;
bool isSubmod() const;
bool isCompatibility() const;
bool isTranslation() const;
bool isVisible() const;
bool isHidden() const;
bool isDisabled() const;
bool isEnabled() const;
bool isAvailable() const;
bool isInstalled() const;
bool isUpdateAvailable() const;
bool isCompatible() const;
bool isKeptDisabled() const;
};

View File

@@ -1,5 +1,5 @@
/*
* cmodmanager.cpp, part of VCMI engine
* modstatecontroller.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@@ -8,13 +8,16 @@
*
*/
#include "StdInc.h"
#include "cmodmanager.h"
#include "modstatecontroller.h"
#include "modstatemodel.h"
#include "../../lib/VCMIDirs.h"
#include "../../lib/filesystem/Filesystem.h"
#include "../../lib/filesystem/CZipLoader.h"
#include "../../lib/modding/CModHandler.h"
#include "../../lib/modding/IdentifierStorage.h"
#include "../../lib/json/JsonNode.h"
#include "../vcmiqt/jsonutils.h"
#include "../vcmiqt/launcherdirs.h"
@@ -61,37 +64,24 @@ QString detectModArchive(QString path, QString modName, std::vector<std::string>
}
CModManager::CModManager(CModList * modList)
ModStateController::ModStateController(std::shared_ptr<ModStateModel> modList)
: modList(modList)
{
modSettings = std::make_shared<ModSettingsStorage>();
loadMods();
loadModSettings();
}
void CModManager::loadModSettings()
ModStateController::~ModStateController() = default;
void ModStateController::loadRepositories(QVector<JsonNode> repomap)
{
modList->setModSettings(modSettings);
modList->setRepositories(repomap);
}
void CModManager::resetRepositories()
{
modList->resetRepositories();
}
void CModManager::loadRepositories(QVector<QVariantMap> repomap)
{
for (auto const & entry : repomap)
modList->addRepository(entry);
modList->reloadRepositories();
}
void CModManager::loadMods()
{
CModHandler handler;
auto installedMods = handler.getAllMods();
localMods.clear();
//void ModStateController::loadMods()
//{
// CModHandler handler;
// auto installedMods = handler.getAllMods();
// localMods.clear();
//
// for(auto modname : installedMods)
// {
// //calculate mod size
@@ -115,43 +105,43 @@ void CModManager::loadMods()
// localMods.insert(modNameQt, mod);
// modSettings->registerNewMod(modNameQt, json["keepDisabled"].Bool());
// }
modList->setLocalModList(localMods);
}
// modList->setLocalModList(localMods);
//}
bool CModManager::addError(QString modname, QString message)
bool ModStateController::addError(QString modname, QString message)
{
recentErrors.push_back(QString("%1: %2").arg(modname).arg(message));
return false;
}
QStringList CModManager::getErrors()
QStringList ModStateController::getErrors()
{
QStringList ret = recentErrors;
recentErrors.clear();
return ret;
}
bool CModManager::installMod(QString modname, QString archivePath)
bool ModStateController::installMod(QString modname, QString archivePath)
{
return canInstallMod(modname) && doInstallMod(modname, archivePath);
}
bool CModManager::uninstallMod(QString modname)
bool ModStateController::uninstallMod(QString modname)
{
return canUninstallMod(modname) && doUninstallMod(modname);
}
bool CModManager::enableMod(QString modname)
bool ModStateController::enableMod(QString modname)
{
return canEnableMod(modname) && doEnableMod(modname, true);
}
bool CModManager::disableMod(QString modname)
bool ModStateController::disableMod(QString modname)
{
return canDisableMod(modname) && doEnableMod(modname, false);
}
bool CModManager::canInstallMod(QString modname)
bool ModStateController::canInstallMod(QString modname)
{
auto mod = modList->getMod(modname);
@@ -163,7 +153,7 @@ bool CModManager::canInstallMod(QString modname)
return true;
}
bool CModManager::canUninstallMod(QString modname)
bool ModStateController::canUninstallMod(QString modname)
{
auto mod = modList->getMod(modname);
@@ -176,11 +166,11 @@ bool CModManager::canUninstallMod(QString modname)
return true;
}
bool CModManager::canEnableMod(QString modname)
bool ModStateController::canEnableMod(QString modname)
{
auto mod = modList->getMod(modname);
if(mod.isEnabled())
if(modList->isModEnabled(modname))
return addError(modname, tr("Mod is already enabled"));
if(!mod.isInstalled())
@@ -192,62 +182,61 @@ bool CModManager::canEnableMod(QString modname)
for(auto modEntry : mod.getDependencies())
{
if(!modList->hasMod(modEntry)) // required mod is not available
if(!modList->isModExists(modEntry)) // required mod is not available
return addError(modname, tr("Required mod %1 is missing").arg(modEntry));
CModEntry modData = modList->getMod(modEntry);
ModState modData = modList->getMod(modEntry);
if(!modData.isCompatibilityPatch() && !modData.isEnabled())
if(!modData.isCompatibility() && !modList->isModEnabled(modEntry))
return addError(modname, tr("Required mod %1 is not enabled").arg(modEntry));
}
for(QString modEntry : modList->getModList())
for(QString modEntry : modList->getAllMods())
{
auto mod = modList->getMod(modEntry);
// "reverse conflict" - enabled mod has this one as conflict
if(mod.isEnabled() && mod.getConflicts().contains(modname))
if(modList->isModEnabled(modname) && mod.getConflicts().contains(modname))
return addError(modname, tr("This mod conflicts with %1").arg(modEntry));
}
for(auto modEntry : mod.getConflicts())
{
// check if conflicting mod installed and enabled
if(modList->hasMod(modEntry) && modList->getMod(modEntry).isEnabled())
if(modList->isModExists(modEntry) && modList->isModEnabled(modEntry))
return addError(modname, tr("This mod conflicts with %1").arg(modEntry));
}
return true;
}
bool CModManager::canDisableMod(QString modname)
bool ModStateController::canDisableMod(QString modname)
{
auto mod = modList->getMod(modname);
if(mod.isDisabled())
if(!modList->isModEnabled(modname))
return addError(modname, tr("Mod is already disabled"));
if(!mod.isInstalled())
return addError(modname, tr("Mod must be installed first"));
for(QString modEntry : modList->getModList())
for(QString modEntry : modList->getAllMods())
{
auto current = modList->getMod(modEntry);
if(current.getDependencies().contains(modname) && current.isEnabled())
if(current.getDependencies().contains(modname) && modList->isModEnabled(modEntry))
return addError(modname, tr("This mod is needed to run %1").arg(modEntry));
}
return true;
}
bool CModManager::doEnableMod(QString mod, bool on)
bool ModStateController::doEnableMod(QString mod, bool on)
{
modSettings->setModActive(mod, on);
modList->modChanged(mod);
//modSettings->setModActive(mod, on);
//modList->modChanged(mod);
return true;
}
bool CModManager::doInstallMod(QString modname, QString archivePath)
bool ModStateController::doInstallMod(QString modname, QString archivePath)
{
const auto destDir = CLauncherDirs::modsPath() + QChar{'/'};
@@ -301,13 +290,13 @@ bool CModManager::doInstallMod(QString modname, QString archivePath)
removeModDir(destDir + upperLevel);
CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &) { return true; });
loadMods();
modList->reloadRepositories();
//loadMods();
//modList->reloadRepositories();
return true;
}
bool CModManager::doUninstallMod(QString modname)
bool ModStateController::doUninstallMod(QString modname)
{
ResourcePath resID(std::string("Mods/") + modname.toStdString(), EResType::DIRECTORY);
// Get location of the mod, in case-insensitive way
@@ -321,13 +310,13 @@ bool CModManager::doUninstallMod(QString modname)
return addError(modname, tr("Mod is located in protected directory, please remove it manually:\n") + modFullDir.absolutePath());
CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &){ return true; });
loadMods();
modList->reloadRepositories();
//loadMods();
//modList->reloadRepositories();
return true;
}
bool CModManager::removeModDir(QString path)
bool ModStateController::removeModDir(QString path)
{
// issues 2673 and 2680 its why you do not recursively remove without sanity check
QDir checkDir(path);

View File

@@ -1,5 +1,5 @@
/*
* cmodmanager.h, part of VCMI engine
* modstatecontroller.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@@ -9,21 +9,25 @@
*/
#pragma once
#include "cmodlist.h"
#include "modsettingsstorage.h"
#include <QVector>
class CModManager : public QObject
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
VCMI_LIB_NAMESPACE_END
class ModStateModel;
class ModStateController : public QObject, public boost::noncopyable
{
Q_OBJECT
CModList * modList;
std::shared_ptr<ModStateModel> modList;
// check-free version of public method
bool doEnableMod(QString mod, bool on);
bool doInstallMod(QString mod, QString archivePath);
bool doUninstallMod(QString mod);
std::shared_ptr<ModSettingsStorage> modSettings;
QVariantMap localMods;
QStringList recentErrors;
@@ -31,12 +35,10 @@ class CModManager : public QObject
bool removeModDir(QString mod);
public:
CModManager(CModList * modList);
ModStateController(std::shared_ptr<ModStateModel> modList);
~ModStateController();
void resetRepositories();
void loadRepositories(QVector<QVariantMap> repomap);
void loadModSettings();
void loadMods();
void loadRepositories(QVector<JsonNode> repositoriesList);
QStringList getErrors();

View File

@@ -1,5 +1,5 @@
/*
* cmodlistmodel_moc.cpp, part of VCMI engine
* modstateview_moc.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@@ -8,7 +8,9 @@
*
*/
#include "StdInc.h"
#include "cmodlistmodel_moc.h"
#include "modstateitemmodel_moc.h"
#include "modstatemodel.h"
#include <QIcon>
@@ -23,12 +25,12 @@ static const QString iconEnabledSubmod = ":/icons/submod-enabled.png";
static const QString iconUpdate = ":/icons/mod-update.png";
}
CModListModel::CModListModel(QObject * parent)
ModStateItemModel::ModStateItemModel(QObject * parent)
: QAbstractItemModel(parent)
{
}
QString CModListModel::modIndexToName(const QModelIndex & index) const
QString ModStateItemModel::modIndexToName(const QModelIndex & index) const
{
if(index.isValid())
{
@@ -38,7 +40,7 @@ QString CModListModel::modIndexToName(const QModelIndex & index) const
}
QString CModListModel::modTypeName(QString modTypeID) const
QString ModStateItemModel::modTypeName(QString modTypeID) const
{
static const QMap<QString, QString> modTypes = {
{"Translation", tr("Translation")},
@@ -71,28 +73,28 @@ QString CModListModel::modTypeName(QString modTypeID) const
return tr("Other");
}
QVariant CModListModel::getValue(const CModEntry & mod, int field) const
QVariant ModStateItemModel::getValue(const ModState & mod, int field) const
{
switch(field)
{
case ModFields::STATUS_ENABLED:
return mod.getModStatus() & (ModStatus::ENABLED | ModStatus::INSTALLED);
return model->isModEnabled(mod.getID());
case ModFields::STATUS_UPDATE:
return mod.getModStatus() & (ModStatus::UPDATEABLE | ModStatus::INSTALLED);
return model->isModUpdateAvailable(mod.getID());
case ModFields::NAME:
return mod.getValue("name");
return mod.getName();
case ModFields::TYPE:
return modTypeName(mod.getValue("modType").toString());
return modTypeName(mod.getType());
default:
return QVariant();
}
}
QVariant CModListModel::getText(const CModEntry & mod, int field) const
QVariant ModStateItemModel::getText(const ModState & mod, int field) const
{
switch(field)
{
@@ -104,49 +106,52 @@ QVariant CModListModel::getText(const CModEntry & mod, int field) const
}
}
QVariant CModListModel::getIcon(const CModEntry & mod, int field) const
QVariant ModStateItemModel::getIcon(const ModState & mod, int field) const
{
if (field == ModFields::STATUS_ENABLED)
{
if (!model->isModInstalled(mod.getID()))
return QVariant();
if(mod.isSubmod())
{
QString toplevelParent = mod.getName().section('.', 0, 0);
if (getMod(toplevelParent).isDisabled())
if (!model->isModEnabled(mod.getTopParentID()))
{
if (mod.isEnabled())
if (model->isModEnabled(mod.getID()))
return QIcon(ModStatus::iconEnabledSubmod);
if(mod.isDisabled())
else
return QIcon(ModStatus::iconDisabledSubmod);
}
}
if (mod.isEnabled())
if (model->isModEnabled(mod.getID()))
return QIcon(ModStatus::iconEnabled);
if(mod.isDisabled())
else
return QIcon(ModStatus::iconDisabled);
}
if(field == ModFields::STATUS_UPDATE)
{
if (mod.isUpdateable())
if (model->isModUpdateAvailable(mod.getID()))
return QIcon(ModStatus::iconUpdate);
if(!mod.isInstalled())
if (!model->isModInstalled(mod.getID()))
return QIcon(ModStatus::iconDownload);
}
return QVariant();
}
QVariant CModListModel::getTextAlign(int field) const
QVariant ModStateItemModel::getTextAlign(int field) const
{
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
}
QVariant CModListModel::data(const QModelIndex & index, int role) const
QVariant ModStateItemModel::data(const QModelIndex & index, int role) const
{
if(index.isValid())
{
auto mod = getMod(modIndexToName(index));
auto mod = model->getMod(modIndexToName(index));
switch(role)
{
@@ -165,24 +170,24 @@ QVariant CModListModel::data(const QModelIndex & index, int role) const
return QVariant();
}
int CModListModel::rowCount(const QModelIndex & index) const
int ModStateItemModel::rowCount(const QModelIndex & index) const
{
if(index.isValid())
return modIndex[modIndexToName(index)].size();
return modIndex[""].size();
}
int CModListModel::columnCount(const QModelIndex &) const
int ModStateItemModel::columnCount(const QModelIndex &) const
{
return ModFields::COUNT;
}
Qt::ItemFlags CModListModel::flags(const QModelIndex &) const
Qt::ItemFlags ModStateItemModel::flags(const QModelIndex &) const
{
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
}
QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int role) const
QVariant ModStateItemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
static const QString header[ModFields::COUNT] =
{
@@ -197,32 +202,23 @@ QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int
return QVariant();
}
void CModListModel::reloadRepositories()
void ModStateItemModel::reloadRepositories()
{
beginResetModel();
endResetModel();
}
void CModListModel::resetRepositories()
void ModStateItemModel::modChanged(QString modID)
{
beginResetModel();
CModList::resetRepositories();
endResetModel();
}
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);
emit dataChanged(createIndex(row, 0, index), createIndex(row, 4, index));
}
void CModListModel::endResetModel()
void ModStateItemModel::endResetModel()
{
modNameToID = getModList();
modNameToID = model->getAllMods();
modIndex.clear();
for(const QString & str : modNameToID)
{
@@ -238,7 +234,7 @@ void CModListModel::endResetModel()
QAbstractItemModel::endResetModel();
}
QModelIndex CModListModel::index(int row, int column, const QModelIndex & parent) const
QModelIndex ModStateItemModel::index(int row, int column, const QModelIndex & parent) const
{
if(parent.isValid())
{
@@ -253,7 +249,7 @@ QModelIndex CModListModel::index(int row, int column, const QModelIndex & parent
return QModelIndex();
}
QModelIndex CModListModel::parent(const QModelIndex & child) const
QModelIndex ModStateItemModel::parent(const QModelIndex & child) const
{
QString modID = modNameToID[child.internalId()];
for(auto entry = modIndex.begin(); entry != modIndex.end(); entry++) // because using range-for entry type is QMap::value_type oO
@@ -266,26 +262,25 @@ QModelIndex CModListModel::parent(const QModelIndex & child) const
return QModelIndex();
}
void CModFilterModel::setTypeFilter(int filteredType, int filterMask)
void CModFilterModel::setTypeFilter(ModFilterMask newFilterMask)
{
this->filterMask = filterMask;
this->filteredType = filteredType;
filterMask = newFilterMask;
invalidateFilter();
}
bool CModFilterModel::filterMatchesThis(const QModelIndex & source) const
{
CModEntry mod = base->getMod(source.data(ModRoles::ModNameRole).toString());
return (mod.getModStatus() & filterMask) == filteredType &&
//QString modID =source.data(ModRoles::ModNameRole).toString();
//ModState mod = base->model->getMod(modID);
return /*(mod.getModStatus() & filterMask) == filteredType &&*/
QSortFilterProxyModel::filterAcceptsRow(source.row(), source.parent());
}
bool CModFilterModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const
{
QModelIndex index = base->index(source_row, 0, source_parent);
CModEntry mod = base->getMod(index.data(ModRoles::ModNameRole).toString());
if (!mod.isVisible())
QString modID = index.data(ModRoles::ModNameRole).toString();
if (base->model->isModVisible(modID))
return false;
if(filterMatchesThis(index))
@@ -309,8 +304,8 @@ bool CModFilterModel::filterAcceptsRow(int source_row, const QModelIndex & sourc
return false;
}
CModFilterModel::CModFilterModel(CModListModel * model, QObject * parent)
: QSortFilterProxyModel(parent), base(model), filteredType(ModStatus::MASK_NONE), filterMask(ModStatus::MASK_NONE)
CModFilterModel::CModFilterModel(ModStateItemModel * model, QObject * parent)
: QSortFilterProxyModel(parent), base(model), filterMask(ModFilterMask::ALL)
{
setSourceModel(model);
setSortRole(ModRoles::ValueRole);

View File

@@ -1,5 +1,5 @@
/*
* cmodlistmodel_moc.h, part of VCMI engine
* modstateview_moc.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@@ -9,11 +9,12 @@
*/
#pragma once
#include "cmodlist.h"
#include <QAbstractTableModel>
#include <QSortFilterProxyModel>
class ModStateModel;
class ModState;
namespace ModFields
{
enum EModFields
@@ -26,6 +27,16 @@ enum EModFields
};
}
enum class ModFilterMask : uint8_t
{
ALL,
AVAILABLE,
INSTALLED,
UPDATEABLE,
ENABLED,
DISABLED
};
namespace ModRoles
{
enum EModRoles
@@ -35,14 +46,17 @@ enum EModRoles
};
}
class CModListModel : public QAbstractItemModel, public CModList
class ModStateItemModel : public QAbstractItemModel
{
friend class CModFilterModel;
Q_OBJECT
QVector<QString> modNameToID;
std::shared_ptr<ModStateModel> model;
QStringList modNameToID;
// contains mapping mod -> numbered list of submods
// mods that have no parent located under "" key (empty string)
QMap<QString, QVector<QString>> modIndex;
QMap<QString, QStringList> modIndex;
void endResetModel();
@@ -50,17 +64,16 @@ class CModListModel : public QAbstractItemModel, public CModList
QString modTypeName(QString modTypeID) const;
QVariant getTextAlign(int field) const;
QVariant getValue(const CModEntry & mod, int field) const;
QVariant getText(const CModEntry & mod, int field) const;
QVariant getIcon(const CModEntry & mod, int field) const;
QVariant getValue(const ModState & mod, int field) const;
QVariant getText(const ModState & mod, int field) const;
QVariant getIcon(const ModState & mod, int field) const;
public:
explicit CModListModel(QObject * parent = nullptr);
explicit ModStateItemModel(QObject * parent = nullptr);
/// CModListContainer overrides
void resetRepositories() override;
void reloadRepositories() override;
void modChanged(QString modID) override;
void reloadRepositories();
void modChanged(QString modID);
QVariant data(const QModelIndex & index, int role) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
@@ -72,25 +85,19 @@ public:
QModelIndex parent(const QModelIndex & child) const override;
Qt::ItemFlags flags(const QModelIndex & index) const override;
signals:
public slots:
};
class CModFilterModel : public QSortFilterProxyModel
{
CModListModel * base;
int filteredType;
int filterMask;
ModStateItemModel * base;
ModFilterMask filterMask;
bool filterMatchesThis(const QModelIndex & source) const;
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override;
public:
void setTypeFilter(int filteredType, int filterMask);
void setTypeFilter(ModFilterMask filterMask);
CModFilterModel(CModListModel * model, QObject * parent = nullptr);
CModFilterModel(ModStateItemModel * model, QObject * parent = nullptr);
};

View File

@@ -0,0 +1,73 @@
/*
* modstatemodel.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 "modstatemodel.h"
#include "../../lib/modding/ModManager.h"
ModStateModel::ModStateModel()
:modManager(std::make_unique<ModManager>())
{}
ModStateModel::~ModStateModel() = default;
void ModStateModel::setRepositories(QVector<JsonNode> repositoriesList)
{
//TODO
}
ModState ModStateModel::getMod(QString modName) const
{
return ModState(modManager->getModDescription(modName.toStdString()));
}
template<typename Container>
QStringList stringListStdToQt(const Container & container)
{
QStringList result;
for (const auto & str : container)
result.push_back(QString::fromStdString(str));
return result;
}
QStringList ModStateModel::getAllMods() const
{
return stringListStdToQt(modManager->getActiveMods());
}
QStringList ModStateModel::getSubmods(QString modName) const
{
return {}; //TODO
}
bool ModStateModel::isModExists(QString modName) const
{
return vstd::contains(modManager->getAllMods(), modName.toStdString());
}
bool ModStateModel::isModInstalled(QString modName) const
{
return getMod(modName).isInstalled();
}
bool ModStateModel::isModEnabled(QString modName) const
{
return getMod(modName).isEnabled(); // TODO
}
bool ModStateModel::isModUpdateAvailable(QString modName) const
{
return getMod(modName).isUpdateAvailable();
}
bool ModStateModel::isModVisible(QString modName) const
{
return getMod(modName).isVisible();
}

View File

@@ -0,0 +1,40 @@
/*
* modstatemodel.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 "modstate.h"
VCMI_LIB_NAMESPACE_BEGIN
class JsonNode;
class ModManager;
VCMI_LIB_NAMESPACE_END
/// Class that represent current state of available mods
/// Provides Qt-based interface to library class ModManager
class ModStateModel
{
std::unique_ptr<ModManager> modManager;
public:
ModStateModel();
~ModStateModel();
void setRepositories(QVector<JsonNode> repositoriesList);
ModState getMod(QString modName) const;
QStringList getAllMods() const;
QStringList getSubmods(QString modName) const;
bool isModExists(QString modName) const;
bool isModInstalled(QString modName) const;
bool isModEnabled(QString modName) const;
bool isModUpdateAvailable(QString modName) const;
bool isModVisible(QString modName) const;
};

View File

@@ -162,48 +162,38 @@ void LibClasses::loadModFilesystem()
logGlobal->info("\tMod filesystems: %d ms", loadTime.getDiff());
}
static void logHandlerLoaded(const std::string & name, CStopWatch & timer)
{
logGlobal->info("\t\t %s handler: %d ms", name, timer.getDiff());
}
template <class Handler> void createHandler(std::shared_ptr<Handler> & handler, const std::string &name, CStopWatch &timer)
template <class Handler> void createHandler(std::shared_ptr<Handler> & handler)
{
handler = std::make_shared<Handler>();
logHandlerLoaded(name, timer);
}
void LibClasses::init(bool onlyEssential)
{
CStopWatch pomtime;
CStopWatch totalTime;
createHandler(settingsHandler, "Game Settings", pomtime);
createHandler(settingsHandler);
modh->initializeConfig();
createHandler(generaltexth, "General text", pomtime);
createHandler(bth, "Bonus type", pomtime);
createHandler(roadTypeHandler, "Road", pomtime);
createHandler(riverTypeHandler, "River", pomtime);
createHandler(terrainTypeHandler, "Terrain", pomtime);
createHandler(heroh, "Hero", pomtime);
createHandler(heroclassesh, "Hero classes", pomtime);
createHandler(arth, "Artifact", pomtime);
createHandler(creh, "Creature", pomtime);
createHandler(townh, "Town", pomtime);
createHandler(biomeHandler, "Obstacle set", pomtime);
createHandler(objh, "Object", pomtime);
createHandler(objtypeh, "Object types information", pomtime);
createHandler(spellh, "Spell", pomtime);
createHandler(skillh, "Skill", pomtime);
createHandler(terviewh, "Terrain view pattern", pomtime);
createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
createHandler(generaltexth);
createHandler(bth);
createHandler(roadTypeHandler);
createHandler(riverTypeHandler);
createHandler(terrainTypeHandler);
createHandler(heroh);
createHandler(heroclassesh);
createHandler(arth);
createHandler(creh);
createHandler(townh);
createHandler(biomeHandler);
createHandler(objh);
createHandler(objtypeh);
createHandler(spellh);
createHandler(skillh);
createHandler(terviewh);
createHandler(tplh); //templates need already resolved identifiers (refactor?)
#if SCRIPTING_ENABLED
createHandler(scriptHandler, "Script", pomtime);
createHandler(scriptHandler);
#endif
createHandler(battlefieldsHandler, "Battlefields", pomtime);
createHandler(obstacleHandler, "Obstacles", pomtime);
logGlobal->info("\tInitializing handlers: %d ms", totalTime.getDiff());
createHandler(battlefieldsHandler);
createHandler(obstacleHandler);
modh->load();
modh->afterLoad(onlyEssential);

View File

@@ -259,8 +259,8 @@ void CModHandler::initializeConfig()
for(const TModID & modName : getActiveMods())
{
const auto & mod = getModInfo(modName);
if (!mod.getConfig()["settings"].isNull())
VLC->settingsHandler->loadBase(mod.getConfig()["settings"]);
if (!mod.getLocalConfig()["settings"].isNull())
VLC->settingsHandler->loadBase(mod.getLocalConfig()["settings"]);
}
}
@@ -271,8 +271,8 @@ void CModHandler::loadTranslation(const TModID & modName)
std::string preferredLanguage = VLC->generaltexth->getPreferredLanguage();
std::string modBaseLanguage = getModInfo(modName).getBaseLanguage();
JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.getConfig()["translations"]);
JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.getConfig()[preferredLanguage]["translations"]);
JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.getLocalConfig()["translations"]);
JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.getLocalConfig()[preferredLanguage]["translations"]);
VLC->generaltexth->loadTranslationOverrides(modName, modBaseLanguage, baseTranslation);
VLC->generaltexth->loadTranslationOverrides(modName, preferredLanguage, extraTranslation);

View File

@@ -297,7 +297,7 @@ void CContentHandler::afterLoadFinalization()
void CContentHandler::preloadData(const ModDescription & mod)
{
preloadModData(mod.getID(), mod.getConfig(), false);
preloadModData(mod.getID(), mod.getLocalConfig(), false);
// bool validate = validateMod(mod);
//

View File

@@ -19,7 +19,7 @@ VCMI_LIB_NAMESPACE_BEGIN
ModDescription::ModDescription(const TModID & fullID, const JsonNode & config)
: identifier(fullID)
, config(std::make_unique<JsonNode>(config))
, localConfig(std::make_unique<JsonNode>(config))
, dependencies(loadModList(config["depends"]))
, softDependencies(loadModList(config["softDepends"]))
, conflicts(loadModList(config["conflicts"]))
@@ -72,27 +72,42 @@ const std::string & ModDescription::getBaseLanguage() const
{
static const std::string defaultLanguage = "english";
return getConfig()["language"].isString() ? getConfig()["language"].String() : defaultLanguage;
return getLocalConfig()["language"].isString() ? getLocalConfig()["language"].String() : defaultLanguage;
}
const std::string & ModDescription::getName() const
{
return getConfig()["name"].String();
return getLocalConfig()["name"].String();
}
const JsonNode & ModDescription::getFilesystemConfig() const
{
return getConfig()["filesystem"];
return getLocalConfig()["filesystem"];
}
const JsonNode & ModDescription::getConfig() const
const JsonNode & ModDescription::getLocalConfig() const
{
return *config;
return *localConfig;
}
const JsonNode & ModDescription::getValue(const std::string & keyName) const
{
return getLocalConfig()[keyName];
}
const JsonNode & ModDescription::getLocalValue(const std::string & keyName) const
{
return getLocalConfig()[keyName];
}
const JsonNode & ModDescription::getRepositoryValue(const std::string & keyName) const
{
return (*repositoryConfig)[keyName];
}
CModVersion ModDescription::getVersion() const
{
return CModVersion::fromString(getConfig()["version"].String());
return CModVersion::fromString(getLocalConfig()["version"].String());
}
ModVerificationInfo ModDescription::getVerificationInfo() const
@@ -108,17 +123,22 @@ ModVerificationInfo ModDescription::getVerificationInfo() const
bool ModDescription::isCompatibility() const
{
return getConfig()["modType"].String() == "Compatibility";
return getLocalConfig()["modType"].String() == "Compatibility";
}
bool ModDescription::isTranslation() const
{
return getConfig()["modType"].String() == "Translation";
return getLocalConfig()["modType"].String() == "Translation";
}
bool ModDescription::keepDisabled() const
{
return getConfig()["keepDisabled"].Bool();
return getLocalConfig()["keepDisabled"].Bool();
}
bool ModDescription::isInstalled() const
{
return !localConfig->isNull();
}
bool ModDescription::affectsGameplay() const

View File

@@ -26,12 +26,14 @@ class DLL_LINKAGE ModDescription : boost::noncopyable
TModSet softDependencies;
TModSet conflicts;
std::unique_ptr<JsonNode> config;
std::unique_ptr<JsonNode> localConfig;
std::unique_ptr<JsonNode> repositoryConfig;
TModSet loadModList(const JsonNode & configNode) const;
public:
ModDescription(const TModID & fullID, const JsonNode & config);
ModDescription(const TModID & fullID, const JsonNode & localConfig);
ModDescription(const TModID & fullID, const JsonNode & localConfig, const JsonNode & repositoryConfig);
~ModDescription();
const TModID & getID() const;
@@ -45,7 +47,10 @@ public:
const std::string & getName() const;
const JsonNode & getFilesystemConfig() const;
const JsonNode & getConfig() const;
const JsonNode & getLocalConfig() const;
const JsonNode & getValue(const std::string & keyName) const;
const JsonNode & getLocalValue(const std::string & keyName) const;
const JsonNode & getRepositoryValue(const std::string & keyName) const;
CModVersion getVersion() const;
ModVerificationInfo getVerificationInfo() const;
@@ -54,6 +59,7 @@ public:
bool isCompatibility() const;
bool isTranslation() const;
bool keepDisabled() const;
bool isInstalled() const;
};
VCMI_LIB_NAMESPACE_END

View File

@@ -203,7 +203,7 @@ std::vector<TModID> ModsPresetState::getActiveMods() const
for(const auto & activeMod : activeRootMods)
{
activeRootMods.push_back(activeMod);
allActiveMods.push_back(activeMod);
for(const auto & submod : getModSettings(activeMod))
if(submod.second)
@@ -212,7 +212,7 @@ std::vector<TModID> ModsPresetState::getActiveMods() const
return allActiveMods;
}
ModsStorage::ModsStorage(const std::vector<TModID> & modsToLoad)
ModsStorage::ModsStorage(const std::vector<TModID> & modsToLoad, const std::vector<JsonNode> & repositoryList)
{
JsonNode coreModConfig(JsonPath::builtin("config/gameConfig.json"));
coreModConfig.setModScope(ModScope::scopeBuiltin());
@@ -245,14 +245,21 @@ const ModDescription & ModsStorage::getMod(const TModID & fullID) const
}
ModManager::ModManager()
:ModManager(std::vector<JsonNode>())
{
}
ModManager::ModManager(const std::vector<JsonNode> & repositoryList)
: modsState(std::make_unique<ModsState>())
, modsPreset(std::make_unique<ModsPresetState>())
{
eraseMissingModsFromPreset();
//TODO: load only active mods & all their submods in game mode
modsStorage = std::make_unique<ModsStorage>(modsState->getAllMods(), repositoryList);
addNewModsToPreset();
std::vector<TModID> desiredModList = modsPreset->getActiveMods();
modsStorage = std::make_unique<ModsStorage>(desiredModList);
generateLoadOrder(desiredModList);
}
@@ -273,6 +280,11 @@ const TModList & ModManager::getActiveMods() const
return activeMods;
}
const TModList & ModManager::getAllMods() const
{
return activeMods; //TODO
}
void ModManager::eraseMissingModsFromPreset()
{
const TModList & installedMods = modsState->getAllMods();
@@ -383,6 +395,7 @@ void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
break;
}
assert(!sortedValidMods.empty());
activeMods = sortedValidMods;
brokenMods = modsToResolve;
}

View File

@@ -69,13 +69,13 @@ class ModsStorage : boost::noncopyable
std::map<TModID, ModDescription> mods;
public:
ModsStorage(const TModList & modsToLoad);
ModsStorage(const TModList & modsToLoad, const std::vector<JsonNode> & repositoryList);
const ModDescription & getMod(const TModID & fullID) const;
};
/// Provides public interface to access mod state
class ModManager : boost::noncopyable
class DLL_LINKAGE ModManager : boost::noncopyable
{
/// all currently active mods, in their load order
TModList activeMods;
@@ -92,11 +92,13 @@ class ModManager : boost::noncopyable
void addNewModsToPreset();
public:
ModManager(const std::vector<JsonNode> & repositoryList);
ModManager();
~ModManager();
const ModDescription & getModDescription(const TModID & modID) const;
const TModList & getActiveMods() const;
const TModList & getAllMods() const;
bool isModActive(const TModID & modID) const;
};