mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Reworked mod handling in Launcher in order to unify code with lib
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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() | ||||
| { | ||||
|   | ||||
| @@ -46,7 +46,7 @@ public: | ||||
| 	explicit MainWindow(QWidget * parent = nullptr); | ||||
| 	~MainWindow() override; | ||||
|  | ||||
| 	const CModList & getModList() const; | ||||
| //	const CModList & getModList() const; | ||||
| 	CModListView * getModView(); | ||||
|  | ||||
| 	void updateTranslation(); | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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; | ||||
| }; | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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(); | ||||
| } | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
							
								
								
									
										212
									
								
								launcher/modManager/modstate.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										212
									
								
								launcher/modManager/modstate.cpp
									
									
									
									
									
										Normal 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(); | ||||
| } | ||||
							
								
								
									
										71
									
								
								launcher/modManager/modstate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								launcher/modManager/modstate.h
									
									
									
									
									
										Normal 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; | ||||
| }; | ||||
| @@ -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); | ||||
| @@ -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(); | ||||
| 
 | ||||
| @@ -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); | ||||
| @@ -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); | ||||
| }; | ||||
							
								
								
									
										73
									
								
								launcher/modManager/modstatemodel.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								launcher/modManager/modstatemodel.cpp
									
									
									
									
									
										Normal 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(); | ||||
| } | ||||
							
								
								
									
										40
									
								
								launcher/modManager/modstatemodel.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								launcher/modManager/modstatemodel.h
									
									
									
									
									
										Normal 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; | ||||
| }; | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
| @@ -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); | ||||
| // | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user