浏览代码

Restored all disabled mod functinality that was used by client

Ivan Savenko 1 年之前
父节点
当前提交
87a665fb7f

+ 0 - 11
client/mainmenu/CMainMenu.cpp

@@ -362,17 +362,6 @@ void CMainMenu::update()
 		menu->switchToTab(menu->getActiveTab());
 	}
 
-	static bool warnedAboutModDependencies = false;
-
-	if (!warnedAboutModDependencies)
-	{
-		warnedAboutModDependencies = true;
-		auto errorMessages = CGI->modh->getModLoadErrors();
-
-		if (!errorMessages.empty())
-			CInfoWindow::showInfoDialog(errorMessages, std::vector<std::shared_ptr<CComponent>>(), PlayerColor(1));
-	}
-
 	// Handles mouse and key input
 	GH.handleEvents();
 	GH.windows().simpleRedraw();

+ 1 - 1
launcher/modManager/cmodlistview_moc.cpp

@@ -809,7 +809,7 @@ void CModListView::installFiles(QStringList files)
 					if(!modJsonUrl.isNull())
 						downloadFile(QString::fromStdString(modName + ".json"), QString::fromStdString(modJsonUrl.String()), tr("mods repository index"));
 
-					repository[modNameLower] = repoData;
+					repository[modNameLower] = modJson;
 				}
 			}
 			else

+ 60 - 62
lib/modding/CModHandler.cpp

@@ -38,19 +38,14 @@ CModHandler::~CModHandler() = default;
 
 std::vector<std::string> CModHandler::getAllMods() const
 {
-	return modManager->getActiveMods();// TODO: currently identical to active
+	return modManager->getAllMods();
 }
 
-std::vector<std::string> CModHandler::getActiveMods() const
+const std::vector<std::string> & CModHandler::getActiveMods() const
 {
 	return modManager->getActiveMods();
 }
 
-std::string CModHandler::getModLoadErrors() const
-{
-	return ""; // TODO: modLoadErrors->toString();
-}
-
 const ModDescription & CModHandler::getModInfo(const TModID & modId) const
 {
 	return modManager->getModDescription(modId);
@@ -86,35 +81,6 @@ static ISimpleResourceLoader * genModFilesystem(const std::string & modName, con
 		return CResourceHandler::createFileSystem(getModDirectory(modName), defaultFS);
 }
 
-//static ui32 calculateModChecksum(const std::string & modName, ISimpleResourceLoader * filesystem)
-//{
-//	boost::crc_32_type modChecksum;
-//	// first - add current VCMI version into checksum to force re-validation on VCMI updates
-//	modChecksum.process_bytes(reinterpret_cast<const void*>(GameConstants::VCMI_VERSION.data()), GameConstants::VCMI_VERSION.size());
-//
-//	// second - add mod.json into checksum because filesystem does not contains this file
-//	// FIXME: remove workaround for core mod
-//	if (modName != ModScope::scopeBuiltin())
-//	{
-//		auto modConfFile = CModInfo::getModFile(modName);
-//		ui32 configChecksum = CResourceHandler::get("initial")->load(modConfFile)->calculateCRC32();
-//		modChecksum.process_bytes(reinterpret_cast<const void *>(&configChecksum), sizeof(configChecksum));
-//	}
-//	// third - add all detected text files from this mod into checksum
-//	auto files = filesystem->getFilteredFiles([](const ResourcePath & resID)
-//	{
-//		return (resID.getType() == EResType::TEXT || resID.getType() == EResType::JSON) &&
-//			   ( boost::starts_with(resID.getName(), "DATA") || boost::starts_with(resID.getName(), "CONFIG"));
-//	});
-//
-//	for (const ResourcePath & file : files)
-//	{
-//		ui32 fileChecksum = filesystem->load(file)->calculateCRC32();
-//		modChecksum.process_bytes(reinterpret_cast<const void *>(&fileChecksum), sizeof(fileChecksum));
-//	}
-//	return modChecksum.checksum();
-//}
-
 void CModHandler::loadModFilesystems()
 {
 	CGeneralTextHandler::detectInstallParameters();
@@ -250,7 +216,7 @@ std::set<TModID> CModHandler::getModEnabledSoftDependencies(const TModID & modId
 {
 	std::set<TModID> softDependencies = getModSoftDependencies(modId);
 
-	vstd::erase_if(softDependencies, [&](const TModID & dependency){ return !modManager->isModActive(dependency);});
+	vstd::erase_if(softDependencies, [this](const TModID & dependency){ return !modManager->isModActive(dependency);});
 
 	return softDependencies;
 }
@@ -285,18 +251,41 @@ void CModHandler::load()
 
 	content->init();
 
-//	for(const TModID & modName : getActiveMods())
-//	{
-//		logMod->trace("Generating checksum for %s", modName);
-//		allMods[modName].updateChecksum(calculateModChecksum(modName, CResourceHandler::get(modName)));
-//	}
+	const auto & activeMods = getActiveMods();
 
-	for(const TModID & modName : getActiveMods())
-		content->preloadData(getModInfo(modName));
+	validationPassed.insert(activeMods.begin(), activeMods.end());
+
+	for(const TModID & modName : activeMods)
+	{
+		modChecksums[modName] = this->modManager->computeChecksum(modName);
+	}
+
+	for(const TModID & modName : activeMods)
+	{
+		const auto & modInfo = getModInfo(modName);
+		bool isValid = content->preloadData(modInfo, isModValidationNeeded(modInfo));
+		if (isValid)
+			logGlobal->info("\t\tParsing mod: OK (%s)", modInfo.getName());
+		else
+			logGlobal->warn("\t\tParsing mod: Issues found! (%s)", modInfo.getName());
+
+		if (!isValid)
+			validationPassed.erase(modName);
+	}
 	logMod->info("\tParsing mod data");
 
-	for(const TModID & modName : getActiveMods())
-		content->load(getModInfo(modName));
+	for(const TModID & modName : activeMods)
+	{
+		const auto & modInfo = getModInfo(modName);
+		bool isValid = content->load(getModInfo(modName), isModValidationNeeded(getModInfo(modName)));
+		if (isValid)
+			logGlobal->info("\t\tLoading mod: OK (%s)", modInfo.getName());
+		else
+			logGlobal->warn("\t\tLoading mod: Issues found! (%s)", modInfo.getName());
+
+		if (!isValid)
+			validationPassed.erase(modName);
+	}
 
 #if SCRIPTING_ENABLED
 	VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load
@@ -304,7 +293,7 @@ void CModHandler::load()
 
 	content->loadCustom();
 
-	for(const TModID & modName : getActiveMods())
+	for(const TModID & modName : activeMods)
 		loadTranslation(modName);
 
 	logMod->info("\tLoading mod data");
@@ -319,21 +308,30 @@ void CModHandler::load()
 
 void CModHandler::afterLoad(bool onlyEssential)
 {
-	//JsonNode modSettings;
-	//for (auto & modEntry : getActiveMods())
-	//{
-	//	std::string pointer = "/" + boost::algorithm::replace_all_copy(modEntry.first, ".", "/mods/");
-
-	//	modSettings["activeMods"].resolvePointer(pointer) = modEntry.second.saveLocalData();
-	//}
-	//modSettings[ModScope::scopeBuiltin()] = coreMod->saveLocalData();
-	//modSettings[ModScope::scopeBuiltin()]["name"].String() = "Original game files";
-
-	//if(!onlyEssential)
-	//{
-	//	std::fstream file(CResourceHandler::get()->getResourceName(ResourcePath("config/modSettings.json"))->c_str(), std::ofstream::out | std::ofstream::trunc);
-	//	file << modSettings.toString();
-	//}
+	JsonNode modSettings;
+	for (auto & modEntry : getActiveMods())
+	{
+		if (validationPassed.count(modEntry))
+			modManager->setValidatedChecksum(modEntry, modChecksums.at(modEntry));
+		else
+			modManager->setValidatedChecksum(modEntry, std::nullopt);
+	}
+
+	modManager->saveConfigurationState();
+}
+
+bool CModHandler::isModValidationNeeded(const ModDescription & mod) const
+{
+	if (settings["mods"]["validation"].String() == "full")
+		return true;
+
+	if (modManager->getValidatedChecksum(mod.getID()) == modChecksums.at(mod.getID()))
+		return false;
+
+	if (settings["mods"]["validation"].String() == "off")
+		return false;
+
+	return true;
 }
 
 VCMI_LIB_NAMESPACE_END

+ 6 - 5
lib/modding/CModHandler.h

@@ -23,12 +23,16 @@ using TModID = std::string;
 class DLL_LINKAGE CModHandler final : boost::noncopyable
 {
 	std::unique_ptr<ModManager> modManager;
+	std::map<std::string, uint32_t> modChecksums;
+	std::set<std::string> validationPassed;
 
 	void loadTranslation(const TModID & modName);
 	void checkModFilesystemsConflicts(const std::map<TModID, ISimpleResourceLoader *> & modFilesystems);
 
+	bool isModValidationNeeded(const ModDescription & mod) const;
+
 public:
-	std::shared_ptr<CContentHandler> content; //FIXME: make private
+	std::shared_ptr<CContentHandler> content;
 
 	/// receives list of available mods and trying to load mod.json from all of them
 	void initializeConfig();
@@ -52,11 +56,8 @@ public:
 
 	/// returns list of all (active) mods
 	std::vector<std::string> getAllMods() const;
-	std::vector<std::string> getActiveMods() const;
+	const std::vector<std::string> & getActiveMods() const;
 
-	/// Returns human-readable string that describes errors encounter during mod loading, such as missing dependencies
-	std::string getModLoadErrors() const;
-	
 	const ModDescription & getModInfo(const TModID & modId) const;
 
 	/// load content from all available mods

+ 11 - 58
lib/modding/ContentTypeHandler.cpp

@@ -55,7 +55,7 @@ ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, const std::string
 
 bool ContentTypeHandler::preloadModData(const std::string & modName, const JsonNode & fileList, bool validate)
 {
-	bool result = false;
+	bool result = true;
 	JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
 	data.setModScope(modName);
 
@@ -259,22 +259,28 @@ void CContentHandler::init()
 	handlers.insert(std::make_pair("biomes", ContentTypeHandler(VLC->biomeHandler.get(), "biome")));
 }
 
-bool CContentHandler::preloadModData(const std::string & modName, JsonNode modConfig, bool validate)
+bool CContentHandler::preloadData(const ModDescription & mod, bool validate)
 {
 	bool result = true;
+	if (validate && mod.getID() != ModScope::scopeBuiltin()) // TODO: remove workaround
+	{
+		if (!JsonUtils::validate(mod.getLocalConfig(), "vcmi:mod", mod.getID()))
+			result = false;
+	}
+
 	for(auto & handler : handlers)
 	{
-		result &= handler.second.preloadModData(modName, modConfig[handler.first], validate);
+		result &= handler.second.preloadModData(mod.getID(), mod.getLocalValue(handler.first), validate);
 	}
 	return result;
 }
 
-bool CContentHandler::loadMod(const std::string & modName, bool validate)
+bool CContentHandler::load(const ModDescription & mod, bool validate)
 {
 	bool result = true;
 	for(auto & handler : handlers)
 	{
-		result &= handler.second.loadMod(modName, validate);
+		result &= handler.second.loadMod(mod.getID(), validate);
 	}
 	return result;
 }
@@ -295,62 +301,9 @@ void CContentHandler::afterLoadFinalization()
 	}
 }
 
-void CContentHandler::preloadData(const ModDescription & mod)
-{
-	preloadModData(mod.getID(), mod.getLocalConfig(), false);
-
-//	bool validate = validateMod(mod);
-//
-//	// print message in format [<8-symbols checksum>] <modname>
-//	auto & info = mod.getVerificationInfo();
-//	logMod->info("\t\t[%08x]%s", info.checksum, info.name);
-//
-//	if (validate && mod.identifier != ModScope::scopeBuiltin())
-//	{
-//		if (!JsonUtils::validate(mod.config, "vcmi:mod", mod.identifier))
-//			mod.validation = CModInfo::FAILED;
-//	}
-//	if (!preloadModData(mod.identifier, mod.config, validate))
-//		mod.validation = CModInfo::FAILED;
-}
-
-void CContentHandler::load(const ModDescription & mod)
-{
-	loadMod(mod.getID(), false);
-
-//	bool validate = validateMod(mod);
-//
-//	if (!loadMod(mod.identifier, validate))
-//		mod.validation = CModInfo::FAILED;
-//
-//	if (validate)
-//	{
-//		if (mod.validation != CModInfo::FAILED)
-//			logMod->info("\t\t[DONE] %s", mod.getVerificationInfo().name);
-//		else
-//			logMod->error("\t\t[FAIL] %s", mod.getVerificationInfo().name);
-//	}
-//	else
-//		logMod->info("\t\t[SKIP] %s", mod.getVerificationInfo().name);
-}
-
 const ContentTypeHandler & CContentHandler::operator[](const std::string & name) const
 {
 	return handlers.at(name);
 }
 
-bool CContentHandler::validateMod(const ModDescription & mod) const
-{
-	if (settings["mods"]["validation"].String() == "full")
-		return true;
-
-//	if (mod.validation == CModInfo::PASSED)
-//		return false;
-
-	if (settings["mods"]["validation"].String() == "off")
-		return false;
-
-	return true;
-}
-
 VCMI_LIB_NAMESPACE_END

+ 2 - 9
lib/modding/ContentTypeHandler.h

@@ -50,23 +50,16 @@ public:
 /// class used to load all game data into handlers. Used only during loading
 class DLL_LINKAGE CContentHandler
 {
-	/// preloads all data from fileList as data from modName.
-	bool preloadModData(const std::string & modName, JsonNode modConfig, bool validate);
-
-	/// actually loads data in mod
-	bool loadMod(const std::string & modName, bool validate);
-
 	std::map<std::string, ContentTypeHandler> handlers;
 
-	bool validateMod(const ModDescription & mod) const;
 public:
 	void init();
 
 	/// preloads all data from fileList as data from modName.
-	void preloadData(const ModDescription & mod);
+	bool preloadData(const ModDescription & mod, bool validateMod);
 
 	/// actually loads data in mod
-	void load(const ModDescription & mod);
+	bool load(const ModDescription & mod, bool validateMod);
 
 	void loadCustom();
 

+ 1 - 1
lib/modding/ModDescription.cpp

@@ -26,7 +26,7 @@ ModDescription::ModDescription(const TModID & fullID, const JsonNode & localConf
 	, conflicts(loadModList(getValue("conflicts")))
 {
 	if(getID() != "core")
-		dependencies.insert("core");
+		dependencies.emplace("core");
 }
 
 ModDescription::~ModDescription() = default;

+ 2 - 1
lib/modding/ModDescription.h

@@ -21,10 +21,11 @@ using TModSet = std::set<TModID>;
 
 class DLL_LINKAGE ModDescription : boost::noncopyable
 {
+	TModID identifier;
+
 	std::unique_ptr<JsonNode> localConfig;
 	std::unique_ptr<JsonNode> repositoryConfig;
 
-	TModID identifier;
 	TModSet dependencies;
 	TModSet softDependencies;
 	TModSet conflicts;

+ 86 - 82
lib/modding/ModManager.cpp

@@ -13,6 +13,7 @@
 #include "ModDescription.h"
 #include "ModScope.h"
 
+#include "../constants/StringConstants.h"
 #include "../filesystem/Filesystem.h"
 #include "../json/JsonNode.h"
 #include "../texts/CGeneralTextHandler.h"
@@ -49,17 +50,45 @@ ModsState::ModsState()
 
 		for(const auto & submod : scanModsDirectory(getModSettingsDirectory(target)))
 			testLocations.push_back(target + '.' + submod);
-
-		// TODO: check that this is vcmi mod and not era mod?
-		// TODO: check that mod name is not reserved (ModScope::isScopeReserved(modFullName)))
 	}
 }
 
-TModList ModsState::getAllMods() const
+TModList ModsState::getInstalledMods() const
 {
 	return modList;
 }
 
+uint32_t ModsState::computeChecksum(const TModID & modName) const
+{
+	boost::crc_32_type modChecksum;
+	// first - add current VCMI version into checksum to force re-validation on VCMI updates
+	modChecksum.process_bytes(reinterpret_cast<const void*>(GameConstants::VCMI_VERSION.data()), GameConstants::VCMI_VERSION.size());
+
+	// second - add mod.json into checksum because filesystem does not contains this file
+	// FIXME: remove workaround for core mod
+	if (modName != ModScope::scopeBuiltin())
+	{
+		auto modConfFile = getModDescriptionFile(modName);
+		ui32 configChecksum = CResourceHandler::get("initial")->load(modConfFile)->calculateCRC32();
+		modChecksum.process_bytes(reinterpret_cast<const void *>(&configChecksum), sizeof(configChecksum));
+	}
+
+	// third - add all detected text files from this mod into checksum
+	const auto & filesystem = CResourceHandler::get(modName);
+
+	auto files = filesystem->getFilteredFiles([](const ResourcePath & resID)
+	{
+		return resID.getType() == EResType::JSON && boost::starts_with(resID.getName(), "CONFIG");
+	});
+
+	for (const ResourcePath & file : files)
+	{
+		ui32 fileChecksum = filesystem->load(file)->calculateCRC32();
+		modChecksum.process_bytes(reinterpret_cast<const void *>(&fileChecksum), sizeof(fileChecksum));
+	}
+	return modChecksum.checksum();
+}
+
 std::vector<TModID> ModsState::scanModsDirectory(const std::string & modDir) const
 {
 	size_t depth = boost::range::count(modDir, '/');
@@ -90,6 +119,9 @@ std::vector<TModID> ModsState::scanModsDirectory(const std::string & modDir) con
 		if(name.find('.') != std::string::npos)
 			continue;
 
+		if (ModScope::isScopeReserved(boost::to_lower_copy(name)))
+			continue;
+
 		if(!CResourceHandler::get("initial")->existsResource(JsonPath::builtin(entry.getName() + "/MOD")))
 			continue;
 
@@ -123,20 +155,14 @@ ModsPresetState::ModsPresetState()
 		else
 			importInitialPreset(); // 1.5 format import
 
-		saveConfiguration(modConfig);
+		saveConfigurationState();
 	}
 }
 
-void ModsPresetState::saveConfiguration(const JsonNode & modSettings)
-{
-	std::fstream file(CResourceHandler::get()->getResourceName(ResourcePath("config/modSettings.json"))->c_str(), std::ofstream::out | std::ofstream::trunc);
-	file << modSettings.toString();
-}
-
 void ModsPresetState::createInitialPreset()
 {
 	// TODO: scan mods directory for all its content? Probably unnecessary since this looks like new install, but who knows?
-	modConfig["presets"]["default"]["mods"].Vector().push_back(JsonNode("vcmi"));
+	modConfig["presets"]["default"]["mods"].Vector().emplace_back("vcmi");
 }
 
 void ModsPresetState::importInitialPreset()
@@ -146,7 +172,7 @@ void ModsPresetState::importInitialPreset()
 	for(const auto & mod : modConfig["activeMods"].Struct())
 	{
 		if(mod.second["active"].Bool())
-			preset["mods"].Vector().push_back(JsonNode(mod.first));
+			preset["mods"].Vector().emplace_back(mod.first);
 
 		for(const auto & submod : mod.second["mods"].Struct())
 			preset["settings"][mod.first][submod.first] = submod.second["active"];
@@ -176,6 +202,15 @@ std::map<TModID, bool> ModsPresetState::getModSettings(const TModID & modID) con
 	return modSettings;
 }
 
+std::optional<uint32_t> ModsPresetState::getValidatedChecksum(const TModID & modName) const
+{
+	const JsonNode & node = modConfig["validatedMods"][modName];
+	if (node.isNull())
+		return std::nullopt;
+	else
+		return node.Integer();
+}
+
 void ModsPresetState::setSettingActiveInPreset(const TModID & modName, const TModID & settingName, bool isActive)
 {
 	const std::string & currentPresetName = modConfig["activePreset"].String();
@@ -212,6 +247,20 @@ std::vector<TModID> ModsPresetState::getActiveMods() const
 	return allActiveMods;
 }
 
+void ModsPresetState::setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value)
+{
+	if (value.has_value())
+		modConfig["validatedMods"][modName].Integer() = *value;
+	else
+		modConfig["validatedMods"].Struct().erase(modName);
+}
+
+void ModsPresetState::saveConfigurationState() const
+{
+	std::fstream file(CResourceHandler::get()->getResourceName(ResourcePath("config/modSettings.json"))->c_str(), std::ofstream::out | std::ofstream::trunc);
+	file << modConfig.toCompactString();
+}
+
 ModsStorage::ModsStorage(const std::vector<TModID> & modsToLoad, const JsonNode & repositoryList)
 {
 	JsonNode coreModConfig(JsonPath::builtin("config/gameConfig.json"));
@@ -221,10 +270,7 @@ ModsStorage::ModsStorage(const std::vector<TModID> & modsToLoad, const JsonNode
 	for(auto modID : modsToLoad)
 	{
 		if(ModScope::isScopeReserved(modID))
-		{
-			logMod->error("Can not load mod %s - this name is reserved for internal use!", modID);
 			continue;
-		}
 
 		JsonNode modConfig(getModDescriptionFile(modID));
 		modConfig.setModScope(modID);
@@ -273,10 +319,10 @@ ModManager::ModManager(const JsonNode & repositoryList)
 	: modsState(std::make_unique<ModsState>())
 	, modsPreset(std::make_unique<ModsPresetState>())
 {
+	//TODO: load only active mods & all their submods in game mode?
+	modsStorage = std::make_unique<ModsStorage>(modsState->getInstalledMods(), repositoryList);
 
 	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();
@@ -301,6 +347,26 @@ const TModList & ModManager::getActiveMods() const
 	return activeMods;
 }
 
+uint32_t ModManager::computeChecksum(const TModID & modName) const
+{
+	return modsState->computeChecksum(modName);
+}
+
+std::optional<uint32_t> ModManager::getValidatedChecksum(const TModID & modName) const
+{
+	return modsPreset->getValidatedChecksum(modName);
+}
+
+void ModManager::setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value)
+{
+	modsPreset->setValidatedChecksum(modName, value);
+}
+
+void ModManager::saveConfigurationState() const
+{
+	modsPreset->saveConfigurationState();
+}
+
 TModList ModManager::getAllMods() const
 {
 	return modsStorage->getAllMods();
@@ -308,7 +374,7 @@ TModList ModManager::getAllMods() const
 
 void ModManager::eraseMissingModsFromPreset()
 {
-	const TModList & installedMods = modsState->getAllMods();
+	const TModList & installedMods = modsState->getInstalledMods();
 	const TModList & rootMods = modsPreset->getActiveRootMods();
 
 	for(const auto & rootMod : rootMods)
@@ -335,7 +401,7 @@ void ModManager::eraseMissingModsFromPreset()
 
 void ModManager::addNewModsToPreset()
 {
-	const TModList & installedMods = modsState->getAllMods();
+	const TModList & installedMods = modsState->getInstalledMods();
 
 	for(const auto & modID : installedMods)
 	{
@@ -421,66 +487,4 @@ void ModManager::generateLoadOrder(std::vector<TModID> modsToResolve)
 	brokenMods = modsToResolve;
 }
 
-//	modLoadErrors = std::make_unique<MetaString>();
-//
-//	auto addErrorMessage = [this](const std::string & textID, const std::string & brokenModID, const std::string & missingModID)
-//	{
-//		modLoadErrors->appendTextID(textID);
-//
-//		if (allMods.count(brokenModID))
-//			modLoadErrors->replaceRawString(allMods.at(brokenModID).getVerificationInfo().name);
-//		else
-//			modLoadErrors->replaceRawString(brokenModID);
-//
-//		if (allMods.count(missingModID))
-//			modLoadErrors->replaceRawString(allMods.at(missingModID).getVerificationInfo().name);
-//		else
-//			modLoadErrors->replaceRawString(missingModID);
-//
-//	};
-//
-//	// Left mods have unresolved dependencies, output all to log.
-//	for(const auto & brokenModID : modsToResolve)
-//	{
-//		const CModInfo & brokenMod = allMods.at(brokenModID);
-//		bool showErrorMessage = false;
-//		for(const TModID & dependency : brokenMod.dependencies)
-//		{
-//			if(!vstd::contains(resolvedModIDs, dependency) && brokenMod.config["modType"].String() != "Compatibility")
-//			{
-//				addErrorMessage("vcmi.server.errors.modNoDependency", brokenModID, dependency);
-//				showErrorMessage = true;
-//			}
-//		}
-//		for(const TModID & conflict : brokenMod.conflicts)
-//		{
-//			if(vstd::contains(resolvedModIDs, conflict))
-//			{
-//				addErrorMessage("vcmi.server.errors.modConflict", brokenModID, conflict);
-//				showErrorMessage = true;
-//			}
-//		}
-//		for(const TModID & reverseConflict : resolvedModIDs)
-//		{
-//			if (vstd::contains(allMods.at(reverseConflict).conflicts, brokenModID))
-//			{
-//				addErrorMessage("vcmi.server.errors.modConflict", brokenModID, reverseConflict);
-//				showErrorMessage = true;
-//			}
-//		}
-//
-//		// some mods may in a (soft) dependency loop.
-//		if(!showErrorMessage && brokenMod.config["modType"].String() != "Compatibility")
-//		{
-//			modLoadErrors->appendTextID("vcmi.server.errors.modDependencyLoop");
-//			if (allMods.count(brokenModID))
-//				modLoadErrors->replaceRawString(allMods.at(brokenModID).getVerificationInfo().name);
-//			else
-//				modLoadErrors->replaceRawString(brokenModID);
-//		}
-//
-//	}
-//	return sortedValidMods;
-//}
-
 VCMI_LIB_NAMESPACE_END

+ 12 - 3
lib/modding/ModManager.h

@@ -31,7 +31,9 @@ class ModsState : boost::noncopyable
 public:
 	ModsState();
 
-	TModList getAllMods() const;
+	TModList getInstalledMods() const;
+
+	uint32_t computeChecksum(const TModID & modName) const;
 };
 
 /// Provides interface to access or change current mod preset
@@ -39,8 +41,6 @@ class ModsPresetState : boost::noncopyable
 {
 	JsonNode modConfig;
 
-	void saveConfiguration(const JsonNode & config);
-
 	void createInitialPreset();
 	void importInitialPreset();
 
@@ -61,6 +61,10 @@ public:
 
 	/// Returns list of all known settings (submods) for a specified mod
 	std::map<TModID, bool> getModSettings(const TModID & modID) const;
+	std::optional<uint32_t> getValidatedChecksum(const TModID & modName) const;
+	void setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value);
+
+	void saveConfigurationState() const;
 };
 
 /// Provides access to mod properties
@@ -101,7 +105,12 @@ public:
 	const ModDescription & getModDescription(const TModID & modID) const;
 	const TModList & getActiveMods() const;
 	TModList getAllMods() const;
+
 	bool isModActive(const TModID & modID) const;
+	uint32_t computeChecksum(const TModID & modName) const;
+	std::optional<uint32_t> getValidatedChecksum(const TModID & modName) const;
+	void setValidatedChecksum(const TModID & modName, std::optional<uint32_t> value);
+	void saveConfigurationState() const;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
vcmiqt/jsonutils.cpp

@@ -116,7 +116,7 @@ JsonNode toJson(QVariant object)
 void jsonToFile(QString filename, const JsonNode & object)
 {
 	std::fstream file(qstringToPath(filename).c_str(), std::ios::out | std::ios_base::binary);
-	file << object.toString();
+	file << object.toCompactString();
 }
 
 }