1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

refactor code: change code style and add some comments

This commit is contained in:
kdmcser 2021-04-10 23:23:58 +08:00
parent 3049c4b70e
commit 10dc6922de
2 changed files with 50 additions and 43 deletions

View File

@ -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")));

View File

@ -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);