1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

fix bug: may load mod before dependeny

This commit is contained in:
kdmcser 2021-04-04 21:54:00 +08:00
parent bc1d99431d
commit 3049c4b70e

View File

@ -716,46 +716,67 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
return true; return true;
} }
std::vector <TModID> CModHandler::resolveDependencies(std::vector <TModID> modsToResolve) const std::vector <TModID> CModHandler::resolveDependencies(std::vector <TModID> input) const
{ {
std::vector<TModID> brokenMods; // Topological sort algorithm
// May not be the fastest one but VCMI does not needs any speed here
// Unless user have dozens of mods with complex dependencies this code should be fine
auto looksValid = [&](const CModInfo & mod) -> bool // first - sort input to have input strictly based on name (and not on hashmap or anything else)
boost::range::sort(input);
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
{ {
auto res = true;
for(const TModID & dependency : mod.dependencies) for(const TModID & dependency : mod.dependencies)
{ {
if(!vstd::contains(modsToResolve, dependency)) if (!vstd::contains(resolvedMods, dependency))
{ return false;
logMod->error("Mod '%s' will not work: it depends on mod '%s', which is not installed.", mod.name, dependency);
res = false; //continue iterations, since we should show all errors for the current mod.
}
} }
return res; return true;
}; };
while(true) while (true)
{ {
for(auto mod : modsToResolve) size_t lastResolvedCount = resolvedMods.size();
std::set <TModID> toResolve; // list of mods resolved on this iteration
for (auto it = input.begin(); it != input.end();)
{ {
if(!looksValid(this->allMods.at(mod))) if (isResolved(allMods.at(*it)))
brokenMods.push_back(mod);
}
if(!brokenMods.empty())
{
vstd::erase_if(modsToResolve, [&](TModID mid)
{ {
return brokenMods.end() != std::find(brokenMods.begin(), brokenMods.end(), mid); toResolve.insert(*it);
}); output.push_back(*it);
brokenMods.clear(); it = input.erase(it);
continue; continue;
}
it++;
} }
break; resolvedMods.insert(toResolve.begin(), toResolve.end());
if (lastResolvedCount == resolvedMods.size())
break; // No more mod can be resolved, should be end.
} }
boost::range::sort(modsToResolve);
return modsToResolve;
// Left mods are broken, output all to log.
for (auto it = input.begin(); it != input.end(); it++)
{
const CModInfo & mod = allMods.at(*it);
for(const TModID & dependency : mod.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);
}
return output;
} }
std::vector<std::string> CModHandler::getModList(std::string path) std::vector<std::string> CModHandler::getModList(std::string path)
{ {
std::string modDir = boost::to_upper_copy(path + "MODS/"); std::string modDir = boost::to_upper_copy(path + "MODS/");