diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index e9ca2e682..0384e9226 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -284,3 +284,8 @@ bool Mod::valid() const { return !m_local_details.mod_id.isEmpty(); } + +QStringList Mod::dependencies() const +{ + return details().dependencies; +} diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index eceb8c256..63257429e 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -72,6 +72,7 @@ class Mod : public Resource { auto loaders() const -> QString; auto mcVersions() const -> QString; auto releaseType() const -> QString; + QStringList dependencies() const; /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index 9195c0368..d866cb1bc 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -142,6 +142,8 @@ struct ModDetails { /* Path of mod logo */ QString icon_file = {}; + QStringList dependencies = {}; + ModDetails() = default; /** Metadata should be handled manually to properly set the mod status. */ @@ -156,6 +158,7 @@ struct ModDetails { , issue_tracker(other.issue_tracker) , licenses(other.licenses) , icon_file(other.icon_file) + , dependencies(other.dependencies) {} ModDetails& operator=(const ModDetails& other) = default; diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index 952115bed..d0eb5949c 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -62,6 +62,36 @@ ModDetails ReadMCModInfo(QByteArray contents) for (auto author : authors) { details.authors.append(author.toString()); } + + if (details.mod_id.startsWith("mod_")) { + details.mod_id = details.mod_id.mid(4); + } + + auto addDep = [&details](QString dep) { + if (dep == "mod_MinecraftForge" || dep == "Forge") + return; + if (dep.contains(":")) { + dep = dep.section(":", 1); + } + if (dep.contains("@")) { + dep = dep.section("@", 0, 0); + } + if (dep.startsWith("mod_")) { + dep = dep.mid(4); + } + details.dependencies.append(dep); + }; + + if (firstObj.contains("requiredMods")) { + for (auto dep : firstObj.value("dependencies").toArray().toVariantList()) { + addDep(dep.toString()); + } + } else if (firstObj.contains("dependencies")) { + for (auto dep : firstObj.value("dependencies").toArray().toVariantList()) { + addDep(dep.toString()); + } + } + return details; }; QJsonParseError jsonError; @@ -199,6 +229,42 @@ ModDetails ReadMCModTOML(QByteArray contents) } details.icon_file = logoFile; + auto parseDep = [&details](toml::array* dependencies) { + if (dependencies) { + for (auto& dep : *dependencies) { + auto dep_table = dep.as_table(); + if (dep_table) { + auto modId = dep_table->get("modId")->value_or(""); + if (modId != "forge" && modId != "neoforge" && modId != "minecraft") { + if (dep_table->contains("type") && (dep_table->get("type"))->value_or("") == "required") { + details.dependencies.append(QString::fromStdString(modId)); + } else if (dep_table->contains("mandatory") && (dep_table->get("mandatory"))->value_or(false)) { + details.dependencies.append(QString::fromStdString(modId)); + } + } + } + } + } + }; + + if (tomlData.contains("dependencies")) { + auto depValue = tomlData["dependencies"]; + if (auto array = depValue.as_array()) { + parseDep(array); + } else if (auto depTable = depValue.as_table()) { + auto expectedKey = details.mod_id.toStdString(); + if (!depTable->contains(expectedKey)) { + for (auto [k, v] : *depTable) { + expectedKey = k; + break; + } + } + if (auto array = (*depTable)[expectedKey].as_array()) { + parseDep(array); + } + } + } + return details; } @@ -286,6 +352,18 @@ ModDetails ReadFabricModInfo(QByteArray contents) details.icon_file = icon.toString(); } } + + if (object.contains("depends")) { + auto depends = object.value("depends"); + if (depends.isObject()) { + auto obj = depends.toObject(); + for (auto key : obj.keys()) { + if (key != "fabricloader" && key != "minecraft" && !key.startsWith("fabric-")) { + details.dependencies.append(key); + } + } + } + } } return details; } @@ -373,6 +451,29 @@ ModDetails ReadQuiltModInfo(QByteArray contents) details.icon_file = icon.toString(); } } + if (object.contains("depends")) { + auto depends = object.value("depends"); + if (depends.isArray()) { + auto array = depends.toArray(); + for (auto obj : array) { + QString modId; + if (obj.isString()) { + modId = obj.toString(); + } else if (obj.isObject()) { + auto objValue = obj.toObject(); + modId = objValue.value("id").toString(); + if (objValue.contains("optional") && objValue.value("optional").toBool()) { + continue; + } + } else { + continue; + } + if (modId != "minecraft" && !modId.startsWith("quilt_")) { + details.dependencies.append(modId); + } + } + } + } } } catch (const Exception& e) {