From 3b725947431b159874e484b8a7bbc097986c28fe Mon Sep 17 00:00:00 2001 From: kdmcser Date: Fri, 25 Oct 2024 23:21:32 +0800 Subject: [PATCH 1/3] add support for soft dependencies --- AUTHORS.h | 1 + Mods/vcmi/config/vcmi/chinese.json | 1 + Mods/vcmi/config/vcmi/english.json | 1 + config/schemas/mod.json | 5 +++ docs/modders/Mod_File_Format.md | 6 +++ lib/modding/CModHandler.cpp | 61 ++++++++++++++++++++++++++++-- lib/modding/CModHandler.h | 2 + lib/modding/CModInfo.cpp | 1 + lib/modding/CModInfo.h | 3 ++ lib/modding/ContentTypeHandler.cpp | 4 +- 10 files changed, 81 insertions(+), 4 deletions(-) diff --git a/AUTHORS.h b/AUTHORS.h index a9ff41e66..fb6552959 100644 --- a/AUTHORS.h +++ b/AUTHORS.h @@ -46,6 +46,7 @@ const std::vector> 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", "" }, diff --git a/Mods/vcmi/config/vcmi/chinese.json b/Mods/vcmi/config/vcmi/chinese.json index 44960a60a..5e23787f1 100644 --- a/Mods/vcmi/config/vcmi/chinese.json +++ b/Mods/vcmi/config/vcmi/chinese.json @@ -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版本不兼容!", diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 2cb7b5772..139a29d3d 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -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!", diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 76d174c28..d98cb4ab3 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -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", diff --git a/docs/modders/Mod_File_Format.md b/docs/modders/Mod_File_Format.md index fd4354fa9..3379ba7f2 100644 --- a/docs/modders/Mod_File_Format.md +++ b/docs/modders/Mod_File_Format.md @@ -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" : diff --git a/lib/modding/CModHandler.cpp b/lib/modding/CModHandler.cpp index 2f2780c5b..f13b134cd 100644 --- a/lib/modding/CModHandler.cpp +++ b/lib/modding/CModHandler.cpp @@ -87,8 +87,9 @@ std::vector CModHandler::validateAndSortDependencies(std::vector sortedValidMods; // Vector keeps order of elements (LIFO) sortedValidMods.reserve(modsToResolve.size()); // push_back calls won't cause memory reallocation std::set resolvedModIDs; // Use a set for validation for performance reason, but set does not keep order of elements + std::set 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 CModHandler::validateAndSortDependencies(std::vector CModHandler::validateAndSortDependencies(std::vector CModHandler::validateAndSortDependencies(std::vector 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 leftResources = modFilesystems[leftModName]->getFilteredFiles(filter); @@ -417,6 +450,28 @@ std::set CModHandler::getModDependencies(const TModID & modId, bool & is return {}; } +std::set 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 CModHandler::getModEnabledSoftDependencies(const TModID & modId) const +{ + std::set 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"])); diff --git a/lib/modding/CModHandler.h b/lib/modding/CModHandler.h index fab2d319b..0f305792f 100644 --- a/lib/modding/CModHandler.h +++ b/lib/modding/CModHandler.h @@ -66,6 +66,8 @@ public: std::set getModDependencies(const TModID & modId) const; std::set getModDependencies(const TModID & modId, bool & isModFound) const; + std::set getModSoftDependencies(const TModID & modId) const; + std::set getModEnabledSoftDependencies(const TModID & modId) const; /// returns list of all (active) mods std::vector getAllMods() const; diff --git a/lib/modding/CModInfo.cpp b/lib/modding/CModInfo.cpp index 66e7421c7..fb0778f6f 100644 --- a/lib/modding/CModInfo.cpp +++ b/lib/modding/CModInfo.cpp @@ -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), diff --git a/lib/modding/CModInfo.h b/lib/modding/CModInfo.h index 3d6b40320..d5c077167 100644 --- a/lib/modding/CModInfo.h +++ b/lib/modding/CModInfo.h @@ -44,6 +44,9 @@ public: /// list of mods that should be loaded before this one std::set 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 softDependencies; + /// list of mods that can't be used in the same time as this one std::set conflicts; diff --git a/lib/modding/ContentTypeHandler.cpp b/lib/modding/ContentTypeHandler.cpp index c5f85add0..ac6bd0abe 100644 --- a/lib/modding/ContentTypeHandler.cpp +++ b/lib/modding/ContentTypeHandler.cpp @@ -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);}); From 123f3923d313027cd59009fe9c3abc6290ff40f4 Mon Sep 17 00:00:00 2001 From: kdmcser Date: Tue, 29 Oct 2024 00:35:03 +0800 Subject: [PATCH 2/3] Update lib/modding/ContentTypeHandler.cpp Co-authored-by: Ivan Savenko --- lib/modding/ContentTypeHandler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/modding/ContentTypeHandler.cpp b/lib/modding/ContentTypeHandler.cpp index ac6bd0abe..2696080fe 100644 --- a/lib/modding/ContentTypeHandler.cpp +++ b/lib/modding/ContentTypeHandler.cpp @@ -201,7 +201,8 @@ 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)); } From 5c5bac12ebe75396c683b17cdda10f140345260b Mon Sep 17 00:00:00 2001 From: kdmcser Date: Tue, 29 Oct 2024 00:35:32 +0800 Subject: [PATCH 3/3] Update lib/modding/CModHandler.cpp Co-authored-by: Ivan Savenko --- lib/modding/CModHandler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modding/CModHandler.cpp b/lib/modding/CModHandler.cpp index f13b134cd..3fc399c13 100644 --- a/lib/modding/CModHandler.cpp +++ b/lib/modding/CModHandler.cpp @@ -137,8 +137,8 @@ std::vector CModHandler::validateAndSortDependencies(std::vector