mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	refactor code: change code style and add some comments
This commit is contained in:
		| @@ -716,64 +716,65 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| std::vector <TModID> CModHandler::resolveDependencies(std::vector <TModID> input) const | // Returned vector affects the resource loaders call order (see CFilesystemList::load). | ||||||
|  | // The loaders call order matters when dependent mod overrides resources in its dependencies. | ||||||
|  | std::vector <TModID> CModHandler::validateAndSortDependencies(std::vector <TModID> modsToResolve) const | ||||||
| { | { | ||||||
| 	// Topological sort algorithm | 	// Topological sort algorithm. | ||||||
| 	// May not be the fastest one but VCMI does not needs any speed here | 	// TODO: Investigate possible ways to improve performance. | ||||||
| 	// Unless user have dozens of mods with complex dependencies this code should be fine | 	boost::range::sort(modsToResolve); // Sort mods per name | ||||||
|  | 	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 | ||||||
|  |  | ||||||
| 	// first - sort input to have input strictly based on name (and not on hashmap or anything else) | 	// Mod is resolved if it has not dependencies or all its dependencies are already resolved | ||||||
| 	boost::range::sort(input); | 	auto isResolved = [&](const CModInfo & mod) -> CModInfo::EValidationStatus | ||||||
|  |  | ||||||
| 	std::vector <TModID> output; |  | ||||||
| 	output.reserve(input.size()); |  | ||||||
|  |  | ||||||
| 	std::set <TModID> resolvedMods; |  | ||||||
|  |  | ||||||
| 	// Check if all mod dependencies are resolved (moved to resolvedMods) |  | ||||||
| 	auto isResolved = [&](const CModInfo & mod) -> bool |  | ||||||
| 	{ | 	{ | ||||||
|  | 		if(mod.dependencies.size() > resolvedModIDs.size()) | ||||||
|  | 			return CModInfo::PENDING; | ||||||
|  |  | ||||||
| 		for(const TModID & dependency : mod.dependencies) | 		for(const TModID & dependency : mod.dependencies) | ||||||
| 		{ | 		{ | ||||||
| 			if (!vstd::contains(resolvedMods, dependency)) | 			if(!vstd::contains(resolvedModIDs, dependency)) | ||||||
| 				return false; | 				return CModInfo::PENDING; | ||||||
| 		} | 		} | ||||||
| 		return true; | 		return CModInfo::PASSED; | ||||||
| 	}; | 	}; | ||||||
|  |  | ||||||
| 	while (true) | 	while(true) | ||||||
| 	{ | 	{ | ||||||
| 		size_t lastResolvedCount = resolvedMods.size(); | 		std::set <TModID> resolvedOnCurrentTreeLevel; | ||||||
| 		std::set <TModID> toResolve; // list of mods resolved on this iteration | 		for(auto it = modsToResolve.begin(); it != modsToResolve.end();) // One iteration - one level of mods tree | ||||||
|  |  | ||||||
| 		for (auto it = input.begin(); it != input.end();) |  | ||||||
| 		{ | 		{ | ||||||
| 			if (isResolved(allMods.at(*it))) | 			if(isResolved(allMods.at(*it)) == CModInfo::PASSED) | ||||||
| 			{ | 			{ | ||||||
| 				toResolve.insert(*it); | 				resolvedOnCurrentTreeLevel.insert(*it); // Not to the resolvedModIDs, so current node childs will be resolved on the next iteration | ||||||
| 				output.push_back(*it); | 				sortedValidMods.push_back(*it); | ||||||
| 				it = input.erase(it); | 				it = modsToResolve.erase(it); | ||||||
| 				continue; | 				continue; | ||||||
| 			} | 			} | ||||||
| 			it++; | 			it++; | ||||||
| 		} | 		} | ||||||
| 		resolvedMods.insert(toResolve.begin(), toResolve.end()); | 		if(resolvedOnCurrentTreeLevel.size()) | ||||||
|  | 		{ | ||||||
| 		if (lastResolvedCount == resolvedMods.size())  | 			resolvedModIDs.insert(resolvedOnCurrentTreeLevel.begin(), resolvedOnCurrentTreeLevel.end()); | ||||||
| 			break;	// No more mod can be resolved, should be end. | 		            continue; | ||||||
|  | 		} | ||||||
|  | 		// If there're no valid mods on the current mods tree level, no more mod can be resolved, should be end. | ||||||
|  | 		break; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	 | 	// Left mods have unresolved dependencies, output all to log. | ||||||
| 	// Left mods are broken, output all to log. | 	for(const auto & brokenModID : modsToResolve) | ||||||
| 	for (auto it = input.begin(); it != input.end(); it++)  |  | ||||||
| 	{ | 	{ | ||||||
| 		const CModInfo & mod = allMods.at(*it); | 		const CModInfo & brokenMod = allMods.at(brokenModID); | ||||||
| 		for(const TModID & dependency : mod.dependencies)  | 		for(const TModID & dependency : brokenMod.dependencies) | ||||||
| 			if(!vstd::contains(resolvedMods, dependency)) | 		{ | ||||||
| 				logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", mod.name, dependency); | 			if(!vstd::contains(resolvedModIDs, dependency)) | ||||||
|  | 				logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", brokenMod.name, dependency); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
|  | 	return sortedValidMods; | ||||||
| 	return output; |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -916,7 +917,7 @@ static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoade | |||||||
|  |  | ||||||
| void CModHandler::loadModFilesystems() | void CModHandler::loadModFilesystems() | ||||||
| { | { | ||||||
| 	activeMods = resolveDependencies(activeMods); | 	activeMods = validateAndSortDependencies(activeMods); | ||||||
|  |  | ||||||
| 	coreMod.updateChecksum(calculateModChecksum("core", CResourceHandler::get("core"))); | 	coreMod.updateChecksum(calculateModChecksum("core", CResourceHandler::get("core"))); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -242,9 +242,15 @@ class DLL_LINKAGE CModHandler | |||||||
| 	// - circular dependencies | 	// - circular dependencies | ||||||
| 	bool checkDependencies(const std::vector <TModID> & input) const; | 	bool checkDependencies(const std::vector <TModID> & input) const; | ||||||
|  |  | ||||||
| 	// returns load order in which all dependencies are resolved, e.g. loaded after required mods | 	/** | ||||||
| 	// function assumes that input list is valid (checkDependencies returned true) | 	* 1. Set apart mods with resolved dependencies from mods which have unresolved dependencies | ||||||
| 	std::vector <TModID> resolveDependencies(std::vector<TModID> input) const; | 	* 2. Sort resolved mods using topological algorithm | ||||||
|  | 	* 3. Log all problem mods and their unresolved dependencies | ||||||
|  | 	* | ||||||
|  | 	* @param modsToResolve list of valid mod IDs (checkDependencies returned true - TODO: Clarify it.) | ||||||
|  | 	* @return a vector of the topologically sorted resolved mods: child nodes (dependent mods) have greater index than parents | ||||||
|  | 	*/ | ||||||
|  | 	std::vector <TModID> validateAndSortDependencies(std::vector <TModID> modsToResolve) const; | ||||||
|  |  | ||||||
| 	std::vector<std::string> getModList(std::string path); | 	std::vector<std::string> getModList(std::string path); | ||||||
| 	void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods); | 	void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user