mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-19 17:12:06 +02:00
add support for soft dependencies
This commit is contained in:
parent
1826b5bbdf
commit
3b72594743
@ -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);});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user