mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	add support for soft dependencies
This commit is contained in:
		| @@ -46,6 +46,7 @@ const std::vector<std::vector<std::string>> contributors = { | ||||
|    { "Developing", "",                     "vmarkovtsev",           ""                             }, | ||||
|    { "Developing", "Tom Zielinski",        "Warmonger",             "Warmonger@vp.pl"              }, | ||||
|    { "Developing", "Xiaomin Ding",         "",                      "dingding303@gmail.com"        }, | ||||
|    { "Developing", "Fenghuang Rumeng",     "kdmcser",               "zqtndfj@gmail.com"             }, | ||||
|  | ||||
|    { "Testing",    "Ben Yan",              "by003",                 "benyan9110@gmail.com,"        }, | ||||
|    { "Testing",    "",                     "Misiokles",             ""                             }, | ||||
|   | ||||
| @@ -149,6 +149,7 @@ | ||||
| 	"vcmi.server.errors.modsToEnable"    : "{需要启用的mod列表}", | ||||
| 	"vcmi.server.errors.modsToDisable"   : "{需要禁用的mod列表}", | ||||
| 	"vcmi.server.errors.modNoDependency" : "读取mod包 {'%s'}失败!\n 需要的mod {'%s'} 没有安装或无效!\n", | ||||
| 	"vcmi.server.errors.modDependencyLoop" : "读取mod包 {'%s'}失败!\n 这个mod可能存在循环(软)依赖!", | ||||
| 	"vcmi.server.errors.modConflict" : "读取的mod包 {'%s'}无法运行!\n 与另一个mod {'%s'}冲突!\n", | ||||
| 	"vcmi.server.errors.unknownEntity" : "加载保存失败! 在保存的游戏中发现未知实体'%s'! 保存可能与当前安装的mod版本不兼容!", | ||||
|  | ||||
|   | ||||
| @@ -159,6 +159,7 @@ | ||||
| 	"vcmi.server.errors.modsToEnable"    : "{Following mods are required}", | ||||
| 	"vcmi.server.errors.modsToDisable"   : "{Following mods must be disabled}", | ||||
| 	"vcmi.server.errors.modNoDependency" : "Failed to load mod {'%s'}!\n It depends on mod {'%s'} which is not active!\n", | ||||
| 	"vcmi.server.errors.modDependencyLoop" : "Failed to load mod {'%s'}!\n It maybe in a (soft) dependency loop.", | ||||
| 	"vcmi.server.errors.modConflict" : "Failed to load mod {'%s'}!\n Conflicts with active mod {'%s'}!\n", | ||||
| 	"vcmi.server.errors.unknownEntity" : "Failed to load save! Unknown entity '%s' found in saved game! Save may not be compatible with currently installed version of mods!", | ||||
| 	 | ||||
|   | ||||
| @@ -122,6 +122,11 @@ | ||||
| 			"description" : "List of mods that are required to run this one", | ||||
| 			"items" : { "type" : "string" } | ||||
| 		}, | ||||
| 		"softDepends" : { | ||||
| 			"type" : "array", | ||||
| 			"description" : "List of mods if they are enabled, should be loaded before this one. This mod will overwrite any conflicting items from its soft dependency mods", | ||||
| 			"items" : { "type" : "string" } | ||||
| 		}, | ||||
| 		"conflicts" : { | ||||
| 			"type" : "array", | ||||
| 			"description" : "List of mods that can't be enabled in the same time as this one", | ||||
|   | ||||
| @@ -48,6 +48,12 @@ | ||||
| 	[ | ||||
| 		"baseMod" | ||||
| 	], | ||||
| 	 | ||||
| 	// List of mods if they are enabled, should be loaded before this one. This mod will overwrite any conflicting items from its soft dependency mods. | ||||
| 	"softDepends" : | ||||
| 	[ | ||||
| 		"baseMod" | ||||
| 	], | ||||
|   | ||||
| 	// List of mods that can't be enabled in the same time as this one | ||||
| 	"conflicts" : | ||||
|   | ||||
| @@ -87,8 +87,9 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI | ||||
| 	std::vector <TModID> sortedValidMods; // Vector keeps order of elements (LIFO) | ||||
| 	sortedValidMods.reserve(modsToResolve.size()); // push_back calls won't cause memory reallocation | ||||
| 	std::set <TModID> resolvedModIDs; // Use a set for validation for performance reason, but set does not keep order of elements | ||||
| 	std::set <TModID> notResolvedModIDs(modsToResolve.begin(), modsToResolve.end()); // Use a set for validation for performance reason | ||||
|  | ||||
| 	// Mod is resolved if it has not dependencies or all its dependencies are already resolved | ||||
| 	// Mod is resolved if it has no dependencies or all its dependencies are already resolved | ||||
| 	auto isResolved = [&](const CModInfo & mod) -> bool | ||||
| 	{ | ||||
| 		if(mod.dependencies.size() > resolvedModIDs.size()) | ||||
| @@ -100,6 +101,12 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI | ||||
| 				return false; | ||||
| 		} | ||||
|  | ||||
| 		for(const TModID & softDependency : mod.softDependencies) | ||||
| 		{ | ||||
| 			if(vstd::contains(notResolvedModIDs, softDependency)) | ||||
| 				return false; | ||||
| 		} | ||||
|  | ||||
| 		for(const TModID & conflict : mod.conflicts) | ||||
| 		{ | ||||
| 			if(vstd::contains(resolvedModIDs, conflict)) | ||||
| @@ -130,9 +137,11 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI | ||||
| 		if(!resolvedOnCurrentTreeLevel.empty()) | ||||
| 		{ | ||||
| 			resolvedModIDs.insert(resolvedOnCurrentTreeLevel.begin(), resolvedOnCurrentTreeLevel.end()); | ||||
| 					continue; | ||||
| 			for(auto it = resolvedOnCurrentTreeLevel.begin(); it != resolvedOnCurrentTreeLevel.end(); it++) | ||||
| 				notResolvedModIDs.erase(*it); | ||||
| 			continue; | ||||
| 		} | ||||
| 		// If there're no valid mods on the current mods tree level, no more mod can be resolved, should be end. | ||||
| 		// If there are no valid mods on the current mods tree level, no more mod can be resolved, should be ended. | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| @@ -158,21 +167,42 @@ std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModI | ||||
| 	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; | ||||
| } | ||||
| @@ -352,6 +382,9 @@ void CModHandler::loadModFilesystems() | ||||
| 				if (getModDependencies(leftModName).count(rightModName) || getModDependencies(rightModName).count(leftModName)) | ||||
| 					continue; | ||||
|  | ||||
| 				if (getModSoftDependencies(leftModName).count(rightModName) || getModSoftDependencies(rightModName).count(leftModName)) | ||||
| 					continue; | ||||
|  | ||||
| 				const auto & filter = [](const ResourcePath &path){return path.getType() != EResType::DIRECTORY;}; | ||||
|  | ||||
| 				std::unordered_set<ResourcePath> leftResources = modFilesystems[leftModName]->getFilteredFiles(filter); | ||||
| @@ -417,6 +450,28 @@ std::set<TModID> CModHandler::getModDependencies(const TModID & modId, bool & is | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| std::set<TModID> CModHandler::getModSoftDependencies(const TModID & modId) const | ||||
| { | ||||
| 	auto it = allMods.find(modId); | ||||
| 	if(it != allMods.end()) | ||||
| 		return it->second.softDependencies; | ||||
| 	logMod->error("Mod not found: '%s'", modId); | ||||
| 	return {}; | ||||
| } | ||||
|  | ||||
| std::set<TModID> CModHandler::getModEnabledSoftDependencies(const TModID & modId) const | ||||
| { | ||||
| 	std::set<TModID> softDependencies = getModSoftDependencies(modId); | ||||
| 	for (auto it = softDependencies.begin(); it != softDependencies.end();) | ||||
| 	{ | ||||
| 		if (allMods.find(*it) == allMods.end()) | ||||
| 			it = softDependencies.erase(it); | ||||
| 		else | ||||
| 			it++; | ||||
| 	} | ||||
| 	return softDependencies; | ||||
| } | ||||
|  | ||||
| void CModHandler::initializeConfig() | ||||
| { | ||||
| 	VLC->settingsHandler->loadBase(JsonUtils::assembleFromFiles(coreMod->config["settings"])); | ||||
|   | ||||
| @@ -66,6 +66,8 @@ public: | ||||
|  | ||||
| 	std::set<TModID> getModDependencies(const TModID & modId) const; | ||||
| 	std::set<TModID> getModDependencies(const TModID & modId, bool & isModFound) const; | ||||
| 	std::set<TModID> getModSoftDependencies(const TModID & modId) const; | ||||
| 	std::set<TModID> getModEnabledSoftDependencies(const TModID & modId) const; | ||||
|  | ||||
| 	/// returns list of all (active) mods | ||||
| 	std::vector<std::string> getAllMods() const; | ||||
|   | ||||
| @@ -43,6 +43,7 @@ CModInfo::CModInfo(): | ||||
| CModInfo::CModInfo(const std::string & identifier, const JsonNode & local, const JsonNode & config): | ||||
| 	identifier(identifier), | ||||
| 	dependencies(readModList(config["depends"])), | ||||
| 	softDependencies(readModList(config["softDepends"])), | ||||
| 	conflicts(readModList(config["conflicts"])), | ||||
| 	explicitlyEnabled(false), | ||||
| 	implicitlyEnabled(true), | ||||
|   | ||||
| @@ -44,6 +44,9 @@ public: | ||||
| 	/// list of mods that should be loaded before this one | ||||
| 	std::set <TModID> dependencies; | ||||
|  | ||||
| 	/// list of mods if they are enabled, should be loaded before this one. this mod will overwrite any conflicting items from its soft dependency mods | ||||
| 	std::set <TModID> softDependencies; | ||||
|  | ||||
| 	/// list of mods that can't be used in the same time as this one | ||||
| 	std::set <TModID> conflicts; | ||||
|  | ||||
|   | ||||
| @@ -201,8 +201,10 @@ void ContentTypeHandler::afterLoadFinalization() | ||||
| 			for (auto const & conflictModEntry: conflictModData.Struct()) | ||||
| 				conflictingMods.insert(conflictModEntry.first); | ||||
|  | ||||
| 			for (auto const & modID : conflictingMods) | ||||
| 			for (auto const & modID : conflictingMods) { | ||||
| 				resolvedConflicts.merge(VLC->modh->getModDependencies(modID)); | ||||
| 				resolvedConflicts.merge(VLC->modh->getModEnabledSoftDependencies(modID)); | ||||
| 			} | ||||
|  | ||||
| 			vstd::erase_if(conflictingMods, [&resolvedConflicts](const std::string & entry){ return resolvedConflicts.count(entry);}); | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user