diff options
| author | TheKodeToad <TheKodeToad@proton.me> | 2023-06-24 14:04:27 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-06-24 14:04:27 +0100 | 
| commit | bcf45c74a1b0b3389c05927637bf8aa95b8e43cf (patch) | |
| tree | ba0519e490c627d5d491cc4ff980921a84d29c41 /launcher | |
| parent | df6d46de9e648284a788a327b87698d58a4fc145 (diff) | |
| parent | 8b576fd2bd442a61092de870b4323c280b04d2d6 (diff) | |
| download | PrismLauncher-bcf45c74a1b0b3389c05927637bf8aa95b8e43cf.tar.gz PrismLauncher-bcf45c74a1b0b3389c05927637bf8aa95b8e43cf.tar.bz2 PrismLauncher-bcf45c74a1b0b3389c05927637bf8aa95b8e43cf.zip | |
Merge pull request #986 from Trial97/develop
Mod dependencies 
Diffstat (limited to 'launcher')
25 files changed, 727 insertions, 67 deletions
| diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index ce2771a4..9bad2a67 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -362,6 +362,8 @@ set(MINECRAFT_SOURCES      minecraft/mod/tasks/LocalWorldSaveParseTask.cpp      minecraft/mod/tasks/LocalResourceParse.h      minecraft/mod/tasks/LocalResourceParse.cpp +    minecraft/mod/tasks/GetModDependenciesTask.h +    minecraft/mod/tasks/GetModDependenciesTask.cpp      # Assets      minecraft/AssetsUtils.h diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h index 09147c8c..2baddf8a 100644 --- a/launcher/ResourceDownloadTask.h +++ b/launcher/ResourceDownloadTask.h @@ -38,6 +38,8 @@ class ResourceDownloadTask : public SequentialTask {      const QString& getFilename() const { return m_pack_version.fileName; }      const QString& getCustomPath() const { return m_custom_target_folder; }      const QVariant& getVersionID() const { return m_pack_version.fileId; } +    const ModPlatform::IndexedVersion& getVersion() const { return m_pack_version; } +    const ModPlatform::ResourceProvider& getProvider() const { return m_pack->provider; }      const QString& getName() const { return m_pack->name; }      ModPlatform::IndexedPack::Ptr getPack() { return m_pack; } diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp new file mode 100644 index 00000000..f8ecdb33 --- /dev/null +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +#include "GetModDependenciesTask.h" + +#include <QDebug> +#include <algorithm> +#include <memory> +#include "Json.h" +#include "QObjectPtr.h" +#include "minecraft/mod/MetadataHandler.h" +#include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" +#include "modplatform/flame/FlameAPI.h" +#include "modplatform/modrinth/ModrinthAPI.h" +#include "tasks/ConcurrentTask.h" +#include "tasks/SequentialTask.h" +#include "ui/pages/modplatform/ModModel.h" +#include "ui/pages/modplatform/flame/FlameResourceModels.h" +#include "ui/pages/modplatform/modrinth/ModrinthResourceModels.h" + +static Version mcVersion(BaseInstance* inst) +{ +    return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion(); +} + +static ResourceAPI::ModLoaderTypes mcLoaders(BaseInstance* inst) +{ +    return static_cast<MinecraftInstance*>(inst)->getPackProfile()->getModLoaders().value(); +} + +GetModDependenciesTask::GetModDependenciesTask(QObject* parent, +                                               BaseInstance* instance, +                                               ModFolderModel* folder, +                                               QList<std::shared_ptr<PackDependency>> selected) +    : SequentialTask(parent, tr("Get dependencies")) +    , m_selected(selected) +    , m_flame_provider{ ModPlatform::ResourceProvider::FLAME, std::make_shared<ResourceDownload::FlameModModel>(*instance), +                        std::make_shared<FlameAPI>() } +    , m_modrinth_provider{ ModPlatform::ResourceProvider::MODRINTH, std::make_shared<ResourceDownload::ModrinthModModel>(*instance), +                           std::make_shared<ModrinthAPI>() } +    , m_version(mcVersion(instance)) +    , m_loaderType(mcLoaders(instance)) +{ +    for (auto mod : folder->allMods()) +        if (auto meta = mod->metadata(); meta) +            m_mods.append(meta); +    prepare(); +}; + +void GetModDependenciesTask::prepare() +{ +    for (auto sel : m_selected) { +        for (auto dep : getDependenciesForVersion(sel->version, sel->pack->provider)) { +            addTask(prepareDependencyTask(dep, sel->pack->provider, 20)); +        } +    } +} + +ModPlatform::Dependency GetModDependenciesTask::getOverride(const ModPlatform::Dependency& dep, +                                                            const ModPlatform::ResourceProvider providerName) +{ +    if (auto isQuilt = m_loaderType & ResourceAPI::Quilt; isQuilt || m_loaderType & ResourceAPI::Fabric) { +        auto overide = ModPlatform::getOverrideDeps(); +        auto over = std::find_if(overide.cbegin(), overide.cend(), [dep, providerName, isQuilt](auto o) { +            return o.provider == providerName && dep.addonId == (isQuilt ? o.fabric : o.quilt); +        }); +        if (over != overide.cend()) { +            return { isQuilt ? over->quilt : over->fabric, dep.type }; +        } +    } +    return dep; +} + +QList<ModPlatform::Dependency> GetModDependenciesTask::getDependenciesForVersion(const ModPlatform::IndexedVersion& version, +                                                                                 const ModPlatform::ResourceProvider providerName) +{ +    QList<ModPlatform::Dependency> c_dependencies; +    for (auto ver_dep : version.dependencies) { +        if (ver_dep.type != ModPlatform::DependencyType::REQUIRED) +            continue; + +        auto isOnlyVersion = providerName == ModPlatform::ResourceProvider::MODRINTH && ver_dep.addonId.toString().isEmpty(); +        if (auto dep = std::find_if(c_dependencies.begin(), c_dependencies.end(), +                                    [&ver_dep, isOnlyVersion](const ModPlatform::Dependency& i) { +                                        return isOnlyVersion ? i.version == ver_dep.version : i.addonId == ver_dep.addonId; +                                    }); +            dep != c_dependencies.end()) +            continue;  // check the current dependency list + +        if (auto dep = std::find_if(m_selected.begin(), m_selected.end(), +                                    [&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) { +                                        return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.version +                                                                                                   : i->pack->addonId == ver_dep.addonId); +                                    }); +            dep != m_selected.end()) +            continue;  // check the selected versions + +        if (auto dep = std::find_if(m_mods.begin(), m_mods.end(), +                                    [&ver_dep, providerName, isOnlyVersion](std::shared_ptr<Metadata::ModStruct> i) { +                                        return i->provider == providerName && +                                               (isOnlyVersion ? i->file_id == ver_dep.version : i->project_id == ver_dep.addonId); +                                    }); +            dep != m_mods.end()) +            continue;  // check the existing mods + +        if (auto dep = std::find_if(m_pack_dependencies.begin(), m_pack_dependencies.end(), +                                    [&ver_dep, providerName, isOnlyVersion](std::shared_ptr<PackDependency> i) { +                                        return i->pack->provider == providerName && (isOnlyVersion ? i->version.version == ver_dep.addonId +                                                                                                   : i->pack->addonId == ver_dep.addonId); +                                    }); +            dep != m_pack_dependencies.end())  // check loaded dependencies +            continue; + +        c_dependencies.append(getOverride(ver_dep, providerName)); +    } +    return c_dependencies; +}; + +Task::Ptr GetModDependenciesTask::getProjectInfoTask(std::shared_ptr<PackDependency> pDep) +{ +    auto provider = pDep->pack->provider == m_flame_provider.name ? m_flame_provider : m_modrinth_provider; +    auto responseInfo = std::make_shared<QByteArray>(); +    auto info = provider.api->getProject(pDep->pack->addonId.toString(), responseInfo); +    QObject::connect(info.get(), &NetJob::succeeded, [responseInfo, provider, pDep] { +        QJsonParseError parse_error{}; +        QJsonDocument doc = QJsonDocument::fromJson(*responseInfo, &parse_error); +        if (parse_error.error != QJsonParseError::NoError) { +            qWarning() << "Error while parsing JSON response for mod info at " << parse_error.offset +                       << " reason: " << parse_error.errorString(); +            qDebug() << *responseInfo; +            return; +        } +        try { +            auto obj = provider.name == ModPlatform::ResourceProvider::FLAME ? Json::requireObject(Json::requireObject(doc), "data") +                                                                             : Json::requireObject(doc); +            provider.mod->loadIndexedPack(*pDep->pack, obj); +        } catch (const JSONValidationError& e) { +            qDebug() << doc; +            qWarning() << "Error while reading mod info: " << e.cause(); +        } +    }); +    return info; +} + +Task::Ptr GetModDependenciesTask::prepareDependencyTask(const ModPlatform::Dependency& dep, +                                                        const ModPlatform::ResourceProvider providerName, +                                                        int level) +{ +    auto pDep = std::make_shared<PackDependency>(); +    pDep->dependency = dep; +    pDep->pack = std::make_shared<ModPlatform::IndexedPack>(); +    pDep->pack->addonId = dep.addonId; +    pDep->pack->provider = providerName; + +    m_pack_dependencies.append(pDep); +    auto provider = providerName == m_flame_provider.name ? m_flame_provider : m_modrinth_provider; + +    auto tasks = makeShared<SequentialTask>( +        this, QString("DependencyInfo: %1").arg(dep.addonId.toString().isEmpty() ? dep.version : dep.addonId.toString())); + +    if (!dep.addonId.toString().isEmpty()) { +        tasks->addTask(getProjectInfoTask(pDep)); +    } + +    ResourceAPI::DependencySearchArgs args = { dep, m_version, m_loaderType }; +    ResourceAPI::DependencySearchCallbacks callbacks; + +    callbacks.on_succeed = [dep, provider, pDep, level, this](auto& doc, auto& pack) { +        try { +            QJsonArray arr; +            if (dep.version.length() != 0 && doc.isObject()) { +                arr.append(doc.object()); +            } else { +                arr = doc.isObject() ? Json::ensureArray(doc.object(), "data") : doc.array(); +            } +            pDep->version = provider.mod->loadDependencyVersions(dep, arr); +            if (!pDep->version.addonId.isValid()) { +                if (m_loaderType & ResourceAPI::Quilt) {  // falback for quilt +                    auto overide = ModPlatform::getOverrideDeps(); +                    auto over = std::find_if(overide.cbegin(), overide.cend(), +                                             [dep, provider](auto o) { return o.provider == provider.name && dep.addonId == o.quilt; }); +                    if (over != overide.cend()) { +                        removePack(dep.addonId); +                        addTask(prepareDependencyTask({ over->fabric, dep.type }, provider.name, level)); +                        return; +                    } +                } +                qWarning() << "Error while reading mod version empty "; +                qDebug() << doc; +                return; +            } +            pDep->version.is_currently_selected = true; +            pDep->pack->versions = { pDep->version }; +            pDep->pack->versionsLoaded = true; + +        } catch (const JSONValidationError& e) { +            qDebug() << doc; +            qWarning() << "Error while reading mod version: " << e.cause(); +            return; +        } +        if (level == 0) { +            qWarning() << "Dependency cycle exeeded"; +            return; +        } +        if (dep.addonId.toString().isEmpty() && !pDep->version.addonId.toString().isEmpty()) { +            pDep->pack->addonId = pDep->version.addonId; +            auto dep = getOverride({ pDep->version.addonId, pDep->dependency.type }, provider.name); +            if (dep.addonId != pDep->version.addonId) { +                removePack(pDep->version.addonId); +                addTask(prepareDependencyTask(dep, provider.name, level)); +            } else +                addTask(getProjectInfoTask(pDep)); +        } +        for (auto dep : getDependenciesForVersion(pDep->version, provider.name)) { +            addTask(prepareDependencyTask(dep, provider.name, level - 1)); +        } +    }; + +    auto version = provider.api->getDependencyVersion(std::move(args), std::move(callbacks)); +    tasks->addTask(version); +    return tasks; +}; + +void GetModDependenciesTask::removePack(const QVariant addonId) +{ +    auto pred = [addonId](const std::shared_ptr<PackDependency>& v) { return v->pack->addonId == addonId; }; +#if QT_VERSION >= QT_VERSION_CHECK(6, 1, 0) +    m_pack_dependencies.removeIf(pred); +#else +    for (auto it = m_pack_dependencies.begin(); it != m_pack_dependencies.end();) +        if (pred(*it)) +            it = m_pack_dependencies.erase(it); +        else +            ++it; +#endif +} diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.h b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h new file mode 100644 index 00000000..50eba6af --- /dev/null +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.h @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* + *  Prism Launcher - Minecraft Launcher + *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + */ + +#pragma once + +#include <QDir> +#include <QEventLoop> +#include <QList> +#include <QVariant> +#include <functional> +#include <memory> + +#include "minecraft/mod/MetadataHandler.h" +#include "minecraft/mod/ModFolderModel.h" +#include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h" +#include "tasks/SequentialTask.h" +#include "tasks/Task.h" +#include "ui/pages/modplatform/ModModel.h" + +class GetModDependenciesTask : public SequentialTask { +    Q_OBJECT +   public: +    using Ptr = shared_qobject_ptr<GetModDependenciesTask>; + +    struct PackDependency { +        ModPlatform::Dependency dependency; +        ModPlatform::IndexedPack::Ptr pack; +        ModPlatform::IndexedVersion version; +        PackDependency() = default; +        PackDependency(const ModPlatform::IndexedPack::Ptr p, const ModPlatform::IndexedVersion& v) +        { +            pack = p; +            version = v; +        } +    }; + +    struct Provider { +        ModPlatform::ResourceProvider name; +        std::shared_ptr<ResourceDownload::ModModel> mod; +        std::shared_ptr<ResourceAPI> api; +    }; + +    explicit GetModDependenciesTask(QObject* parent, +                                    BaseInstance* instance, +                                    ModFolderModel* folder, +                                    QList<std::shared_ptr<PackDependency>> selected); + +    auto getDependecies() const -> QList<std::shared_ptr<PackDependency>> { return m_pack_dependencies; } + +   protected slots: +    Task::Ptr prepareDependencyTask(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider, int); +    QList<ModPlatform::Dependency> getDependenciesForVersion(const ModPlatform::IndexedVersion&, +                                                             const ModPlatform::ResourceProvider providerName); +    void prepare(); +    Task::Ptr getProjectInfoTask(std::shared_ptr<PackDependency> pDep); +    ModPlatform::Dependency getOverride(const ModPlatform::Dependency&, const ModPlatform::ResourceProvider providerName); +    void removePack(const QVariant addonId); + +   private: +    QList<std::shared_ptr<PackDependency>> m_pack_dependencies; +    QList<std::shared_ptr<Metadata::ModStruct>> m_mods; +    QList<std::shared_ptr<PackDependency>> m_selected; +    Provider m_flame_provider; +    Provider m_modrinth_provider; + +    Version m_version; +    ResourceAPI::ModLoaderTypes m_loaderType; +}; diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index cc4e252c..4352fad9 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -1,25 +1,24 @@  // SPDX-License-Identifier: GPL-3.0-only  /* -*  PolyMC - Minecraft Launcher -*  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> -*  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> -* -*  This program is free software: you can redistribute it and/or modify -*  it under the terms of the GNU General Public License as published by -*  the Free Software Foundation, version 3. -* -*  This program is distributed in the hope that it will be useful, -*  but WITHOUT ANY WARRANTY; without even the implied warranty of -*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -*  GNU General Public License for more details. -* -*  You should have received a copy of the GNU General Public License -*  along with this program.  If not, see <https://www.gnu.org/licenses/>. -*/ + *  Prism Launcher - Minecraft Launcher + *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + *  Copyright (C) 2022 Sefa Eyeoglu <contact@scrumplex.net> + * + *  This program is free software: you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation, version 3. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program.  If not, see <https://www.gnu.org/licenses/>. + */  #include "LocalModUpdateTask.h" -#include "Application.h"  #include "FileSystem.h"  #include "minecraft/mod/MetadataHandler.h" diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 82da2ab2..3b0a03a1 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -1,7 +1,8 @@  // SPDX-License-Identifier: GPL-3.0-only  /* - *  PolyMC - Minecraft Launcher + *  Prism Launcher - Minecraft Launcher   *  Copyright (c) 2022 flowln <flowlnlnln@gmail.com> + *  Copyright (c) 2023 Trial97 <alexandru.tripon97@gmail.com>   *   *  This program is free software: you can redistribute it and/or modify   *  it under the terms of the GNU General Public License as published by @@ -33,6 +34,8 @@ enum class ResourceProvider { MODRINTH, FLAME };  enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK }; +enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN }; +  class ProviderCapabilities {     public:      auto name(ResourceProvider) -> const char*; @@ -52,6 +55,12 @@ struct DonationData {      QString url;  }; +struct Dependency { +    QVariant addonId; +    DependencyType type; +    QString version; +}; +  struct IndexedVersion {      QVariant addonId;      QVariant fileId; @@ -66,6 +75,7 @@ struct IndexedVersion {      QString hash;      bool is_preferred = true;      QString changelog; +    QList<Dependency> dependencies;      // For internal use, not provided by APIs      bool is_currently_selected = false; @@ -119,6 +129,22 @@ struct IndexedPack {      }  }; +struct OverrideDep { +    QString quilt; +    QString fabric; +    QString slug; +    ModPlatform::ResourceProvider provider; +}; + +inline auto getOverrideDeps() -> QList<OverrideDep> +{ +    return { { "634179", "306612", "API", ModPlatform::ResourceProvider::FLAME }, +             { "720410", "308769", "KotlinLibraries", ModPlatform::ResourceProvider::FLAME }, + +             { "qvIfYCYJ", "P7dR8mSH", "API", ModPlatform::ResourceProvider::MODRINTH }, +             { "lwVhp9o5", "Ha28R6CL", "KotlinLibraries", ModPlatform::ResourceProvider::MODRINTH } }; +}; +  }  // namespace ModPlatform  Q_DECLARE_METATYPE(ModPlatform::IndexedPack) diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index e24971d5..d3277761 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -111,6 +111,16 @@ class ResourceAPI {          std::function<void(QJsonDocument&, ModPlatform::IndexedPack)> on_succeed;      }; +    struct DependencySearchArgs { +        ModPlatform::Dependency dependency; +        Version mcVersion; +        ModLoaderTypes loader; +    }; + +    struct DependencySearchCallbacks { +        std::function<void(QJsonDocument&, const ModPlatform::Dependency&)> on_succeed; +    }; +     public:      /** Gets a list of available sorting methods for this API. */      [[nodiscard]] virtual auto getSortingMethods() const -> QList<SortingMethod> = 0; @@ -143,6 +153,12 @@ class ResourceAPI {          return nullptr;      } +    [[nodiscard]] virtual Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const +    { +        qWarning() << "TODO"; +        return nullptr; +    } +      static auto getModLoaderString(ModLoaderType type) -> const QString      {          switch (type) { diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 49ba12b0..a0611957 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -4,8 +4,10 @@  #pragma once +#include <algorithm>  #include <memory>  #include "modplatform/ModIndex.h" +#include "modplatform/ResourceAPI.h"  #include "modplatform/helpers/NetworkResourceAPI.h"  class FlameAPI : public NetworkResourceAPI { @@ -75,14 +77,44 @@ class FlameAPI : public NetworkResourceAPI {      [[nodiscard]] std::optional<QString> getVersionsURL(VersionSearchArgs const& args) const override      { -        QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(args.pack.addonId.toString()) }; +        auto mappedModLoader = getMappedModLoader(args.loaders.value()); +        auto addonId = args.pack.addonId.toString(); +        if (args.loaders.value() & Quilt) { +            auto overide = ModPlatform::getOverrideDeps(); +            auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { +                return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; +            }); +            if (over != overide.cend()) { +                mappedModLoader = 5; +            } +        } +        QString url{ QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&").arg(addonId) };          QStringList get_parameters;          if (args.mcVersions.has_value())              get_parameters.append(QString("gameVersion=%1").arg(args.mcVersions.value().front().toString()));          if (args.loaders.has_value()) -            get_parameters.append(QString("modLoaderType=%1").arg(getMappedModLoader(args.loaders.value()))); +            get_parameters.append(QString("modLoaderType=%1").arg(mappedModLoader));          return url + get_parameters.join('&');      }; + +    [[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override +    { +        auto mappedModLoader = getMappedModLoader(args.loader); +        auto addonId = args.dependency.addonId.toString(); +        if (args.loader & Quilt) { +            auto overide = ModPlatform::getOverrideDeps(); +            auto over = std::find_if(overide.cbegin(), overide.cend(), [addonId](auto dep) { +                return dep.provider == ModPlatform::ResourceProvider::FLAME && addonId == dep.quilt; +            }); +            if (over != overide.cend()) { +                mappedModLoader = 5; +            } +        } +        return QString("https://api.curseforge.com/v1/mods/%1/files?pageSize=10000&gameVersion=%2&modLoaderType=%3") +            .arg(addonId) +            .arg(args.mcVersion.toString()) +            .arg(mappedModLoader); +    };  }; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 7498e830..227ce489 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -39,15 +39,15 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)      auto links_obj = Json::ensureObject(obj, "links");      pack.extraData.issuesUrl = Json::ensureString(links_obj, "issuesUrl"); -    if(pack.extraData.issuesUrl.endsWith('/')) +    if (pack.extraData.issuesUrl.endsWith('/'))          pack.extraData.issuesUrl.chop(1);      pack.extraData.sourceUrl = Json::ensureString(links_obj, "sourceUrl"); -    if(pack.extraData.sourceUrl.endsWith('/')) +    if (pack.extraData.sourceUrl.endsWith('/'))          pack.extraData.sourceUrl.chop(1);      pack.extraData.wikiUrl = Json::ensureString(links_obj, "wikiUrl"); -    if(pack.extraData.wikiUrl.endsWith('/')) +    if (pack.extraData.wikiUrl.endsWith('/'))          pack.extraData.wikiUrl.chop(1);      if (!pack.extraData.body.isEmpty()) @@ -56,7 +56,7 @@ void FlameMod::loadURLs(ModPlatform::IndexedPack& pack, QJsonObject& obj)  void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)  { -    pack.extraData.body  = api.getModDescription(pack.addonId.toInt()); +    pack.extraData.body = api.getModDescription(pack.addonId.toInt());      if (!pack.extraData.issuesUrl.isEmpty() || !pack.extraData.sourceUrl.isEmpty() || !pack.extraData.wikiUrl.isEmpty())          pack.extraDataLoaded = true; @@ -64,12 +64,12 @@ void FlameMod::loadBody(ModPlatform::IndexedPack& pack, QJsonObject& obj)  static QString enumToString(int hash_algorithm)  { -    switch(hash_algorithm){ -    default: -    case 1: -        return "sha1"; -    case 2: -        return "md5"; +    switch (hash_algorithm) { +        default: +        case 1: +            return "sha1"; +        case 2: +            return "md5";      }  } @@ -84,12 +84,12 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack,      for (auto versionIter : arr) {          auto obj = versionIter.toObject(); -         +          auto file = loadIndexedPackVersion(obj); -        if(!file.addonId.isValid()) +        if (!file.addonId.isValid())              file.addonId = pack.addonId; -        if(file.fileId.isValid()) // Heuristic to check if the returned value is valid +        if (file.fileId.isValid())  // Heuristic to check if the returned value is valid              unsortedVersions.append(file);      } @@ -136,8 +136,61 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) ->          }      } -    if(load_changelog) +    auto dependencies = Json::ensureArray(obj, "dependencies"); +    for (auto d : dependencies) { +        auto dep = Json::ensureObject(d); +        ModPlatform::Dependency dependency; +        dependency.addonId = Json::requireInteger(dep, "modId"); +        switch (Json::requireInteger(dep, "relationType")) { +            case 1:  // EmbeddedLibrary +                dependency.type = ModPlatform::DependencyType::EMBEDDED; +                break; +            case 2:  // OptionalDependency +                dependency.type = ModPlatform::DependencyType::OPTIONAL; +                break; +            case 3:  // RequiredDependency +                dependency.type = ModPlatform::DependencyType::REQUIRED; +                break; +            case 4:  // Tool +                dependency.type = ModPlatform::DependencyType::TOOL; +                break; +            case 5:  // Incompatible +                dependency.type = ModPlatform::DependencyType::INCOMPATIBLE; +                break; +            case 6:  // Include +                dependency.type = ModPlatform::DependencyType::INCLUDE; +                break; +            default: +                dependency.type = ModPlatform::DependencyType::UNKNOWN; +                break; +        } +        file.dependencies.append(dependency); +    } + +    if (load_changelog)          file.changelog = api.getModFileChangelog(file.addonId.toInt(), file.fileId.toInt());      return file;  } + +ModPlatform::IndexedVersion FlameMod::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) +{ +    QVector<ModPlatform::IndexedVersion> versions; +    for (auto versionIter : arr) { +        auto obj = versionIter.toObject(); + +        auto file = loadIndexedPackVersion(obj); +        if (!file.addonId.isValid()) +            file.addonId = m.addonId; + +        if (file.fileId.isValid())  // Heuristic to check if the returned value is valid +            versions.append(file); +    } + +    auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { +        // dates are in RFC 3339 format +        return a.date > b.date; +    }; +    std::sort(versions.begin(), versions.end(), orderSortPredicate); +    return versions.front(); +}; diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h index 33c4a529..aa0d6f81 100644 --- a/launcher/modplatform/flame/FlameModIndex.h +++ b/launcher/modplatform/flame/FlameModIndex.h @@ -6,8 +6,8 @@  #include "modplatform/ModIndex.h" -#include "BaseInstance.h"  #include <QNetworkAccessManager> +#include "BaseInstance.h"  namespace FlameMod { @@ -19,5 +19,5 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,                               const shared_qobject_ptr<QNetworkAccessManager>& network,                               const BaseInstance* inst);  auto loadIndexedPackVersion(QJsonObject& obj, bool load_changelog = false) -> ModPlatform::IndexedVersion; - -}  // namespace FlameMod +auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion; +}  // namespace FlameMod
\ No newline at end of file diff --git a/launcher/modplatform/flame/FlamePackIndex.h b/launcher/modplatform/flame/FlamePackIndex.h index 1ca0fc0e..b089b722 100644 --- a/launcher/modplatform/flame/FlamePackIndex.h +++ b/launcher/modplatform/flame/FlamePackIndex.h @@ -4,6 +4,7 @@  #include <QMetaType>  #include <QString>  #include <QVector> +#include "modplatform/ModIndex.h"  namespace Flame { @@ -27,8 +28,7 @@ struct ModpackExtra {      QString sourceUrl;  }; -struct IndexedPack -{ +struct IndexedPack {      int addonId;      QString name;      QString description; @@ -43,9 +43,9 @@ struct IndexedPack      ModpackExtra extra;  }; -void loadIndexedPack(IndexedPack & m, QJsonObject & obj); +void loadIndexedPack(IndexedPack& m, QJsonObject& obj);  void loadIndexedInfo(IndexedPack&, QJsonObject&); -void loadIndexedPackVersions(IndexedPack & m, QJsonArray & arr); -} +void loadIndexedPackVersions(IndexedPack& m, QJsonArray& arr); +}  // namespace Flame  Q_DECLARE_METATYPE(Flame::IndexedPack) diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.cpp b/launcher/modplatform/helpers/NetworkResourceAPI.cpp index 0ed2410e..c278f800 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.cpp +++ b/launcher/modplatform/helpers/NetworkResourceAPI.cpp @@ -117,3 +117,32 @@ Task::Ptr NetworkResourceAPI::getProject(QString addonId, std::shared_ptr<QByteA      return netJob;  } + +Task::Ptr NetworkResourceAPI::getDependencyVersion(DependencySearchArgs&& args, DependencySearchCallbacks&& callbacks) const +{ +    auto versions_url_optional = getDependencyURL(args); +    if (!versions_url_optional.has_value()) +        return nullptr; + +    auto versions_url = versions_url_optional.value(); + +    auto netJob = makeShared<NetJob>(QString("%1::Dependency").arg(args.dependency.addonId.toString()), APPLICATION->network()); +    auto response = std::make_shared<QByteArray>(); + +    netJob->addNetAction(Net::Download::makeByteArray(versions_url, response)); + +    QObject::connect(netJob.get(), &NetJob::succeeded, [=] { +        QJsonParseError parse_error{}; +        QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); +        if (parse_error.error != QJsonParseError::NoError) { +            qWarning() << "Error while parsing JSON response for getting versions at " << parse_error.offset +                       << " reason: " << parse_error.errorString(); +            qWarning() << *response; +            return; +        } + +        callbacks.on_succeed(doc, args.dependency); +    }); + +    return netJob; +}; diff --git a/launcher/modplatform/helpers/NetworkResourceAPI.h b/launcher/modplatform/helpers/NetworkResourceAPI.h index 84604e41..b72e8253 100644 --- a/launcher/modplatform/helpers/NetworkResourceAPI.h +++ b/launcher/modplatform/helpers/NetworkResourceAPI.h @@ -15,9 +15,11 @@ class NetworkResourceAPI : public ResourceAPI {      Task::Ptr getProjectInfo(ProjectInfoArgs&&, ProjectInfoCallbacks&&) const override;      Task::Ptr getProjectVersions(VersionSearchArgs&&, VersionSearchCallbacks&&) const override; +    Task::Ptr getDependencyVersion(DependencySearchArgs&&, DependencySearchCallbacks&&) const override;     protected:      [[nodiscard]] virtual auto getSearchURL(SearchArgs const& args) const -> std::optional<QString> = 0;      [[nodiscard]] virtual auto getInfoURL(QString const& id) const -> std::optional<QString> = 0;      [[nodiscard]] virtual auto getVersionsURL(VersionSearchArgs const& args) const -> std::optional<QString> = 0; +    [[nodiscard]] virtual auto getDependencyURL(DependencySearchArgs const& args) const -> std::optional<QString> = 0;  }; diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 98f20b51..e83ed2bf 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -38,7 +38,7 @@ class ModrinthAPI : public NetworkResourceAPI {      static auto getModLoaderStrings(const ModLoaderTypes types) -> const QStringList      {          QStringList l; -        for (auto loader : {Forge, Fabric, Quilt}) { +        for (auto loader : { Forge, Fabric, Quilt }) {              if (types & loader) {                  l << getModLoaderString(loader);              } @@ -51,8 +51,7 @@ class ModrinthAPI : public NetworkResourceAPI {      static auto getModLoaderFilters(ModLoaderTypes types) -> const QString      {          QStringList l; -        for (auto loader : getModLoaderStrings(types)) -        { +        for (auto loader : getModLoaderStrings(types)) {              l << QString("\"categories:%1\"").arg(loader);          }          return l.join(','); @@ -135,16 +134,22 @@ class ModrinthAPI : public NetworkResourceAPI {      auto getGameVersionsArray(std::list<Version> mcVersions) const -> QString      {          QString s; -        for(auto& ver : mcVersions){ +        for (auto& ver : mcVersions) {              s += QString("\"versions:%1\",").arg(ver.toString());          } -        s.remove(s.length() - 1, 1); //remove last comma +        s.remove(s.length() - 1, 1);  // remove last comma          return s.isEmpty() ? QString() : s;      } -    inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool -    { -        return loaders & (Forge | Fabric | Quilt); -    } +    inline auto validateModLoaders(ModLoaderTypes loaders) const -> bool { return loaders & (Forge | Fabric | Quilt); } +    [[nodiscard]] std::optional<QString> getDependencyURL(DependencySearchArgs const& args) const override +    { +        return args.dependency.version.length() != 0 ? QString("%1/version/%2").arg(BuildConfig.MODRINTH_PROD_URL, args.dependency.version) +                                                     : QString("%1/project/%2/version?game_versions=[\"%3\"]&loaders=[\"%4\"]") +                                                           .arg(BuildConfig.MODRINTH_PROD_URL) +                                                           .arg(args.dependency.addonId.toString()) +                                                           .arg(args.mcVersion.toString()) +                                                           .arg(getModLoaderStrings(args.loader).join("\",\"")); +    };  }; diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 7ade131e..b4037349 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -22,7 +22,7 @@  #include "Json.h"  #include "minecraft/MinecraftInstance.h"  #include "minecraft/PackProfile.h" -#include "net/NetJob.h" +#include "modplatform/ModIndex.h"  static ModrinthAPI api;  static ModPlatform::ProviderCapabilities ProviderCaps; @@ -140,6 +140,28 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t      file.version_number = Json::requireString(obj, "version_number");      file.changelog = Json::requireString(obj, "changelog"); +    auto dependencies = Json::ensureArray(obj, "dependencies"); +    for (auto d : dependencies) { +        auto dep = Json::ensureObject(d); +        ModPlatform::Dependency dependency; +        dependency.addonId = Json::ensureString(dep, "project_id"); +        dependency.version = Json::ensureString(dep, "version_id"); +        auto depType = Json::requireString(dep, "dependency_type"); + +        if (depType == "required") +            dependency.type = ModPlatform::DependencyType::REQUIRED; +        else if (depType == "optional") +            dependency.type = ModPlatform::DependencyType::OPTIONAL; +        else if (depType == "incompatible") +            dependency.type = ModPlatform::DependencyType::INCOMPATIBLE; +        else if (depType == "embedded") +            dependency.type = ModPlatform::DependencyType::EMBEDDED; +        else +            dependency.type = ModPlatform::DependencyType::UNKNOWN; + +        file.dependencies.append(dependency); +    } +      auto files = Json::requireArray(obj, "files");      int i = 0; @@ -195,3 +217,22 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t      return {};  } + +auto Modrinth::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +{ +    QVector<ModPlatform::IndexedVersion> versions; + +    for (auto versionIter : arr) { +        auto obj = versionIter.toObject(); +        auto file = loadIndexedPackVersion(obj); + +        if (file.fileId.isValid())  // Heuristic to check if the returned value is valid +            versions.append(file); +    } +    auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { +        // dates are in RFC 3339 format +        return a.date > b.date; +    }; +    std::sort(versions.begin(), versions.end(), orderSortPredicate); +    return versions.length() != 0 ? versions.front() : ModPlatform::IndexedVersion(); +}
\ No newline at end of file diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index e73e4b18..a8d986c5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -19,8 +19,8 @@  #include "modplatform/ModIndex.h" -#include "BaseInstance.h"  #include <QNetworkAccessManager> +#include "BaseInstance.h"  namespace Modrinth { @@ -31,5 +31,6 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack,                               const shared_qobject_ptr<QNetworkAccessManager>& network,                               const BaseInstance* inst);  auto loadIndexedPackVersion(QJsonObject& obj, QString hash_type = "sha512", QString filename_prefer = "") -> ModPlatform::IndexedVersion; +auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion;  }  // namespace Modrinth diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 6d90480f..4f59f560 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -18,6 +18,8 @@   */  #include "ResourceDownloadDialog.h" +#include <QEventLoop> +#include <QList>  #include <QPushButton>  #include <algorithm> @@ -30,6 +32,10 @@  #include "minecraft/mod/ShaderPackFolderModel.h"  #include "minecraft/mod/TexturePackFolderModel.h" +#include "minecraft/mod/tasks/GetModDependenciesTask.h" +#include "modplatform/ModIndex.h" +#include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/ProgressDialog.h"  #include "ui/dialogs/ReviewMessageBox.h"  #include "ui/pages/modplatform/ResourcePage.h" @@ -117,18 +123,71 @@ void ResourceDownloadDialog::connectButtons()      connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help);  } +static ModPlatform::ProviderCapabilities ProviderCaps; + +QStringList getRequiredBy(QList<ResourceDownloadDialog::DownloadTaskPtr> tasks, ResourceDownloadDialog::DownloadTaskPtr pack) +{ +    auto addonId = pack->getPack()->addonId; +    auto provider = pack->getPack()->provider; +    auto version = pack->getVersionID(); +    auto req = QStringList(); +    for (auto& task : tasks) { +        if (provider != task->getPack()->provider) +            continue; +        auto deps = task->getVersion().dependencies; +        if (auto dep = std::find_if(deps.begin(), deps.end(), +                                    [addonId, provider, version](const ModPlatform::Dependency& d) { +                                        return d.type == ModPlatform::DependencyType::REQUIRED && +                                               (provider == ModPlatform::ResourceProvider::MODRINTH && d.addonId.toString().isEmpty() +                                                    ? version == d.version +                                                    : d.addonId == addonId); +                                    }); +            dep != deps.end()) { +            req.append(task->getName()); +        } +    } +    return req; +} +  void ResourceDownloadDialog::confirm()  { +    auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); +    confirm_dialog->retranslateUi(resourcesString()); + +    if (auto task = getModDependenciesTask(); task) { +        connect(task.get(), &Task::failed, this, +                [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + +        connect(task.get(), &Task::succeeded, this, [&]() { +            QStringList warnings = task->warnings(); +            if (warnings.count()) { +                CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); +            } +        }); + +        // Check for updates +        ProgressDialog progress_dialog(this); +        progress_dialog.setSkipButton(true, tr("Abort")); +        progress_dialog.setWindowTitle(tr("Checking for dependencies...")); +        auto ret = progress_dialog.execWithTask(task.get()); + +        // If the dialog was skipped / some download error happened +        if (ret == QDialog::DialogCode::Rejected) { +            QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); +            return; +        } else { +            for (auto dep : task->getDependecies()) +                addResource(dep->pack, dep->version); +        } +    } +      auto selected = getTasks();      std::sort(selected.begin(), selected.end(), [](const DownloadTaskPtr& a, const DownloadTaskPtr& b) {          return QString::compare(a->getName(), b->getName(), Qt::CaseInsensitive) < 0;      }); - -    auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); -    confirm_dialog->retranslateUi(resourcesString()); -      for (auto& task : selected) { -        confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath() }); +        confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), +                                         ProviderCaps.name(task->getProvider()), getRequiredBy(selected, task) });      }      if (confirm_dialog->exec()) { @@ -231,6 +290,19 @@ QList<BasePage*> ModDownloadDialog::getPages()      return pages;  } +GetModDependenciesTask::Ptr ModDownloadDialog::getModDependenciesTask() +{ +    if (auto model = dynamic_cast<ModFolderModel*>(getBaseModel().get()); model) { +        QList<std::shared_ptr<GetModDependenciesTask::PackDependency>> selectedVers; +        for (auto& selected : getTasks()) { +            selectedVers.append(std::make_shared<GetModDependenciesTask::PackDependency>(selected->getPack(), selected->getVersion())); +        } + +        return makeShared<GetModDependenciesTask>(this, m_instance, model, selectedVers); +    } +    return nullptr; +}; +  ResourcePackDownloadDialog::ResourcePackDownloadDialog(QWidget* parent,                                                         const std::shared_ptr<ResourcePackFolderModel>& resource_packs,                                                         BaseInstance* instance) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 5b5b48c6..f65daaa3 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -25,6 +25,7 @@  #include <QLayout>  #include "QObjectPtr.h" +#include "minecraft/mod/tasks/GetModDependenciesTask.h"  #include "modplatform/ModIndex.h"  #include "ui/pages/BasePageProvider.h" @@ -81,6 +82,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider {      [[nodiscard]] virtual QString geometrySaveKey() const { return ""; }      void setButtonStatus(); +    [[nodiscard]] virtual GetModDependenciesTask::Ptr getModDependenciesTask() { return nullptr; } +     protected:      const std::shared_ptr<ResourceFolderModel> m_base_model; @@ -103,6 +106,7 @@ class ModDownloadDialog final : public ResourceDownloadDialog {      [[nodiscard]] QString geometrySaveKey() const override { return "ModDownloadGeometry"; }      QList<BasePage*> getPages() override; +    GetModDependenciesTask::Ptr getModDependenciesTask() override;     private:      BaseInstance* m_instance; diff --git a/launcher/ui/dialogs/ReviewMessageBox.cpp b/launcher/ui/dialogs/ReviewMessageBox.cpp index 7b2df278..7b33765f 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.cpp +++ b/launcher/ui/dialogs/ReviewMessageBox.cpp @@ -40,7 +40,8 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)      auto filenameItem = new QTreeWidgetItem(itemTop);      filenameItem->setText(0, tr("Filename: %1").arg(info.filename)); -    itemTop->insertChildren(0, { filenameItem }); +    auto childIndx = 0; +    itemTop->insertChildren(childIndx++, { filenameItem });      if (!info.custom_file_path.isEmpty()) {          auto customPathItem = new QTreeWidgetItem(itemTop); @@ -49,7 +50,31 @@ void ReviewMessageBox::appendResource(ResourceInformation&& info)          itemTop->insertChildren(1, { customPathItem });          itemTop->setIcon(1, QIcon(APPLICATION->getThemedIcon("status-yellow"))); -        itemTop->setToolTip(1, tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it.")); +        itemTop->setToolTip( +            childIndx++, +            tr("This file will be downloaded to a folder location different from the default, possibly due to its loader requiring it.")); +    } + +    auto providerItem = new QTreeWidgetItem(itemTop); +    providerItem->setText(0, tr("Provider: %1").arg(info.provider)); + +    itemTop->insertChildren(childIndx++, { providerItem }); + +    if (!info.required_by.isEmpty()) { +        auto requiredByItem = new QTreeWidgetItem(itemTop); +        if (info.required_by.length() == 1) { +            requiredByItem->setText(0, tr("Required by: %1").arg(info.required_by.back())); +        } else { +            requiredByItem->setText(0, tr("Required by:")); +            auto i = 0; +            for (auto req : info.required_by) { +                auto reqItem = new QTreeWidgetItem(requiredByItem); +                reqItem->setText(0, req); +                reqItem->insertChildren(i++, { reqItem }); +            } +        } + +        itemTop->insertChildren(childIndx++, { requiredByItem });      }      ui->modTreeWidget->addTopLevelItem(itemTop); diff --git a/launcher/ui/dialogs/ReviewMessageBox.h b/launcher/ui/dialogs/ReviewMessageBox.h index 5ec2bc23..a520cc2a 100644 --- a/launcher/ui/dialogs/ReviewMessageBox.h +++ b/launcher/ui/dialogs/ReviewMessageBox.h @@ -13,9 +13,11 @@ class ReviewMessageBox : public QDialog {      static auto create(QWidget* parent, QString&& title, QString&& icon = "") -> ReviewMessageBox*;      using ResourceInformation = struct res_info { -        QString name;   -        QString filename;   -        QString custom_file_path {}; +        QString name; +        QString filename; +        QString custom_file_path{}; +        QString provider; +        QStringList required_by;      };      void appendResource(ResourceInformation&& info); diff --git a/launcher/ui/pages/modplatform/ModModel.h b/launcher/ui/pages/modplatform/ModModel.h index 805d8b9a..dd187aa8 100644 --- a/launcher/ui/pages/modplatform/ModModel.h +++ b/launcher/ui/pages/modplatform/ModModel.h @@ -32,6 +32,7 @@ class ModModel : public ResourceModel {      void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;      void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override = 0;      void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override = 0; +    virtual ModPlatform::IndexedVersion loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) = 0;      void setFilter(std::shared_ptr<ModFilterWidget::Filter> filter) { m_filter = filter; } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp index 667a52d0..0fb67c50 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.cpp @@ -29,6 +29,11 @@ void FlameModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonAr      FlameMod::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);  } +auto FlameModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +{ +    return FlameMod::loadDependencyVersions(m, arr); +}; +  auto FlameModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray  {      return Json::ensureArray(obj.object(), "data"); @@ -81,7 +86,7 @@ void FlameTexturePackModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m,          auto const& mc_versions = version.mcVersion;          if (std::any_of(mc_versions.constBegin(), mc_versions.constEnd(), -                        [this](auto const& mc_version){ return Version(mc_version) <= maximumTexturePackVersion(); })) +                        [this](auto const& mc_version) { return Version(mc_version) <= maximumTexturePackVersion(); }))              filtered_versions.push_back(version);      } diff --git a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h index 221c8f7a..6cfd6a6f 100644 --- a/launcher/ui/pages/modplatform/flame/FlameResourceModels.h +++ b/launcher/ui/pages/modplatform/flame/FlameResourceModels.h @@ -24,6 +24,7 @@ class FlameModModel : public ModModel {      void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;      void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;      void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; +    auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;      auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;  }; diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp index 7f857485..8aa64989 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.cpp @@ -42,12 +42,17 @@ void ModrinthModModel::loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJso      ::Modrinth::loadIndexedPackVersions(m, arr, APPLICATION->network(), &m_base_instance);  } +auto ModrinthModModel::loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion +{ +    return ::Modrinth::loadDependencyVersions(m, arr); +}; +  auto ModrinthModModel::documentToArray(QJsonDocument& obj) const -> QJsonArray  {      return obj.object().value("hits").toArray();  } -ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base)  : ResourcePackResourceModel(base, new ModrinthAPI){} +ModrinthResourcePackModel::ModrinthResourcePackModel(const BaseInstance& base) : ResourcePackResourceModel(base, new ModrinthAPI) {}  void ModrinthResourcePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)  { @@ -69,7 +74,7 @@ auto ModrinthResourcePackModel::documentToArray(QJsonDocument& obj) const -> QJs      return obj.object().value("hits").toArray();  } -ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base)  : TexturePackResourceModel(base, new ModrinthAPI){} +ModrinthTexturePackModel::ModrinthTexturePackModel(const BaseInstance& base) : TexturePackResourceModel(base, new ModrinthAPI) {}  void ModrinthTexturePackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)  { @@ -91,7 +96,7 @@ auto ModrinthTexturePackModel::documentToArray(QJsonDocument& obj) const -> QJso      return obj.object().value("hits").toArray();  } -ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base)  : ShaderPackResourceModel(base, new ModrinthAPI){} +ModrinthShaderPackModel::ModrinthShaderPackModel(const BaseInstance& base) : ShaderPackResourceModel(base, new ModrinthAPI) {}  void ModrinthShaderPackModel::loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj)  { diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h index 66461807..d7c858f8 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthResourceModels.h @@ -40,6 +40,7 @@ class ModrinthModModel : public ModModel {      void loadIndexedPack(ModPlatform::IndexedPack& m, QJsonObject& obj) override;      void loadExtraPackInfo(ModPlatform::IndexedPack& m, QJsonObject& obj) override;      void loadIndexedPackVersions(ModPlatform::IndexedPack& m, QJsonArray& arr) override; +    auto loadDependencyVersions(const ModPlatform::Dependency& m, QJsonArray& arr) -> ModPlatform::IndexedVersion override;      auto documentToArray(QJsonDocument& obj) const -> QJsonArray override;  }; | 
