diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b341840df..bc48abdef 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1031,8 +1031,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/BlockedModsDialog.h ui/dialogs/ChooseProviderDialog.h ui/dialogs/ChooseProviderDialog.cpp - ui/dialogs/ModUpdateDialog.cpp - ui/dialogs/ModUpdateDialog.h + ui/dialogs/ResourceUpdateDialog.cpp + ui/dialogs/ResourceUpdateDialog.h ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.h diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 1ba09c8ec..d5735dcb8 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -89,35 +89,35 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const default: break; } - return at(row)->version(); + return at(row).version(); } case DateColumn: return m_resources[row]->dateTimeChanged(); case ProviderColumn: - return at(row)->provider(); + return at(row).provider(); default: return QVariant(); } case Qt::ToolTipRole: if (column == NAME_COLUMN) { - if (at(row)->isSymLinkUnder(instDirPath())) { + if (at(row).isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath()); + .arg(at(row).fileinfo().canonicalFilePath()); } - if (at(row)->isMoreThanOneHardLink()) { + if (at(row).isMoreThanOneHardLink()) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } } return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + return at(row).icon({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } @@ -129,7 +129,7 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const case Qt::CheckStateRole: switch (column) { case ActiveColumn: - return at(row)->enabled() ? Qt::Checked : Qt::Unchecked; + return at(row).enabled() ? Qt::Checked : Qt::Unchecked; default: return QVariant(); } @@ -190,29 +190,6 @@ bool ModFolderModel::isValid() return m_dir.exists() && m_dir.isReadable(); } -auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList -{ - QList selected_resources; - for (auto i : indexes) { - if (i.column() != 0) - continue; - - selected_resources.push_back(at(i.row())); - } - return selected_resources; -} - -auto ModFolderModel::allMods() -> QList -{ - QList mods; - - for (auto& res : qAsConst(m_resources)) { - mods.append(static_cast(res.get())); - } - - return mods; -} - void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) { auto iter = m_active_parse_tasks.constFind(ticket); diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index c1db33c0c..cd5e99b11 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -76,9 +76,6 @@ class ModFolderModel : public ResourceFolderModel { bool isValid(); - auto selectedMods(QModelIndexList& indexes) -> QList; - auto allMods() -> QList; - RESOURCE_HELPERS(Mod) private slots: diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index ac81681cf..30b453812 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -184,7 +184,7 @@ auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attem return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } -auto Resource::destroyMetadata(const QDir& index_dir) -> bool +auto Resource::destroyMetadata(const QDir& index_dir) -> void { if (metadata()) { Metadata::remove(index_dir, metadata()->slug); diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 0d77be7c9..772f456f1 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -101,7 +101,7 @@ class Resource : public QObject { // Delete all files of this resource. auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; // Delete the metadata only. - auto destroyMetadata(const QDir& index_dir) -> bool; + auto destroyMetadata(const QDir& index_dir) -> void; [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index ff5e95983..a7a80db05 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -242,10 +242,10 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes) return true; } -bool ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes) +void ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes) { if (indexes.isEmpty()) - return true; + return; for (auto i : indexes) { if (i.column() != 0) @@ -256,8 +256,6 @@ bool ResourceFolderModel::deleteMetadata(const QModelIndexList& indexes) } update(); - - return true; } bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, EnableAction action) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 8b5ac68ad..3de3c5476 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -19,6 +19,58 @@ class QSortFilterProxyModel; +/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */ +#define RESOURCE_HELPERS(T) \ + [[nodiscard]] T& operator[](int index) \ + { \ + return *static_cast(m_resources[index].get()); \ + } \ + [[nodiscard]] T& at(int index) \ + { \ + return *static_cast(m_resources[index].get()); \ + } \ + [[nodiscard]] const T& at(int index) const \ + { \ + return *static_cast(m_resources.at(index).get()); \ + } \ + [[nodiscard]] T& first() \ + { \ + return *static_cast(m_resources.first().get()); \ + } \ + [[nodiscard]] T& last() \ + { \ + return *static_cast(m_resources.last().get()); \ + } \ + [[nodiscard]] T* find(QString id) \ + { \ + auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \ + [&](Resource::Ptr const& r) { return r->internal_id() == id; }); \ + if (iter == m_resources.constEnd()) \ + return nullptr; \ + return static_cast((*iter).get()); \ + } \ + QList selected##T##s(const QModelIndexList& indexes) \ + { \ + QList result; \ + for (const QModelIndex& index : indexes) { \ + if (index.column() != 0) \ + continue; \ + \ + result.append(&at(index.row())); \ + } \ + return result; \ + } \ + QList all##T##s() \ + { \ + QList result; \ + result.reserve(m_resources.size()); \ + \ + for (const Resource::Ptr& resource : m_resources) \ + result.append(static_cast(resource.get())); \ + \ + return result; \ + } + /** A basic model for external resources. * * This model manages a list of resources. As such, external users of such resources do not own them, @@ -69,7 +121,7 @@ class ResourceFolderModel : public QAbstractListModel { */ virtual bool uninstallResource(QString file_name, bool preserve_metadata = false); virtual bool deleteResources(const QModelIndexList&); - virtual bool deleteMetadata(const QModelIndexList&); + virtual void deleteMetadata(const QModelIndexList&); /** Applies the given 'action' to the resources in 'indexes'. * @@ -85,9 +137,7 @@ class ResourceFolderModel : public QAbstractListModel { [[nodiscard]] qsizetype size() const { return m_resources.size(); } [[nodiscard]] bool empty() const { return size() == 0; } - [[nodiscard]] Resource& at(int index) { return *m_resources.at(index); } - [[nodiscard]] Resource const& at(int index) const { return *m_resources.at(index); } - [[nodiscard]] QList const& all() const { return m_resources; } + RESOURCE_HELPERS(Resource) [[nodiscard]] QDir const& dir() const { return m_dir; } @@ -232,37 +282,6 @@ class ResourceFolderModel : public QAbstractListModel { std::atomic m_next_resolution_ticket = 0; }; -/* A macro to define useful functions to handle Resource* -> T* more easily on derived classes */ -#define RESOURCE_HELPERS(T) \ - [[nodiscard]] T* operator[](int index) \ - { \ - return static_cast(m_resources[index].get()); \ - } \ - [[nodiscard]] T* at(int index) \ - { \ - return static_cast(m_resources[index].get()); \ - } \ - [[nodiscard]] const T* at(int index) const \ - { \ - return static_cast(m_resources.at(index).get()); \ - } \ - [[nodiscard]] T* first() \ - { \ - return static_cast(m_resources.first().get()); \ - } \ - [[nodiscard]] T* last() \ - { \ - return static_cast(m_resources.last().get()); \ - } \ - [[nodiscard]] T* find(QString id) \ - { \ - auto iter = std::find_if(m_resources.constBegin(), m_resources.constEnd(), \ - [&](Resource::Ptr const& r) { return r->internal_id() == id; }); \ - if (iter == m_resources.constEnd()) \ - return nullptr; \ - return static_cast((*iter).get()); \ - } - /* Template definition to avoid some code duplication */ template void ResourceFolderModel::applyUpdates(QSet& current_set, QSet& new_set, QMap& new_resources) diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 8d0cd5fcd..b777dbafd 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -72,12 +72,12 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case NameColumn: return m_resources[row]->name(); case PackFormatColumn: { - auto resource = at(row); - auto pack_format = resource->packFormat(); + auto& resource = at(row); + auto pack_format = resource.packFormat(); if (pack_format == 0) return tr("Unrecognized"); - auto version_bounds = resource->compatibleVersions(); + auto version_bounds = resource.compatibleVersions(); if (version_bounds.first.toString().isEmpty()) return QString::number(pack_format); @@ -92,10 +92,10 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const return {}; } case Qt::DecorationRole: { - if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } @@ -105,14 +105,14 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); } if (column == NameColumn) { - if (at(row)->isSymLinkUnder(instDirPath())) { + if (at(row).isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath()); + .arg(at(row).fileinfo().canonicalFilePath()); ; } - if (at(row)->isMoreThanOneHardLink()) { + if (at(row).isMoreThanOneHardLink()) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } @@ -127,7 +127,7 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const case Qt::CheckStateRole: switch (column) { case ActiveColumn: - return at(row)->enabled() ? Qt::Checked : Qt::Unchecked; + return at(row).enabled() ? Qt::Checked : Qt::Unchecked; default: return {}; } diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index b4b06a62a..3795795a2 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -52,11 +52,6 @@ TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* in m_columnsHideable = { false, true, false, true, true }; } -Task* TexturePackFolderModel::createUpdateTask() -{ - return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared(entry); }); -} - Task* TexturePackFolderModel::createParseTask(Resource& resource) { return new LocalTexturePackParseTask(m_next_resolution_ticket, static_cast(resource)); @@ -84,14 +79,14 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const } case Qt::ToolTipRole: if (column == NameColumn) { - if (at(row)->isSymLinkUnder(instDirPath())) { + if (at(row).isSymLinkUnder(instDirPath())) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is symbolically linked from elsewhere. Editing it will also change the original." "\nCanonical Path: %1") - .arg(at(row)->fileinfo().canonicalFilePath()); + .arg(at(row).fileinfo().canonicalFilePath()); ; } - if (at(row)->isMoreThanOneHardLink()) { + if (at(row).isMoreThanOneHardLink()) { return m_resources[row]->internal_id() + tr("\nWarning: This resource is hard linked elsewhere. Editing it will also change the original."); } @@ -99,10 +94,10 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NameColumn && (at(row)->isSymLinkUnder(instDirPath()) || at(row)->isMoreThanOneHardLink())) + if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); if (column == ImageColumn) { - return at(row)->image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); + return at(row).image({ 32, 32 }, Qt::AspectRatioMode::KeepAspectRatioByExpanding); } return {}; } diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 8bd83d988..aaa997c16 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -13,13 +13,13 @@ class CheckUpdateTask : public Task { Q_OBJECT public: - CheckUpdateTask(QList& mods, + CheckUpdateTask(QList& resources, std::list& mcVersions, std::optional loaders, - std::shared_ptr mods_folder) - : Task(nullptr), m_mods(mods), m_game_versions(mcVersions), m_loaders(loaders), m_mods_folder(mods_folder){}; + std::shared_ptr resource_model) + : Task(nullptr), m_resources(resources), m_game_versions(mcVersions), m_loaders(loaders), m_resource_model(resource_model){}; - struct UpdatableMod { + struct Update { QString name; QString old_hash; QString old_version; @@ -30,14 +30,14 @@ class CheckUpdateTask : public Task { shared_qobject_ptr download; public: - UpdatableMod(QString name, - QString old_h, - QString old_v, - QString new_v, - std::optional new_v_type, - QString changelog, - ModPlatform::ResourceProvider p, - shared_qobject_ptr t) + Update(QString name, + QString old_h, + QString old_v, + QString new_v, + std::optional new_v_type, + QString changelog, + ModPlatform::ResourceProvider p, + shared_qobject_ptr t) : name(name) , old_hash(old_h) , old_version(old_v) @@ -49,7 +49,7 @@ class CheckUpdateTask : public Task { {} }; - auto getUpdatable() -> std::vector&& { return std::move(m_updatable); } + auto getUpdates() -> std::vector&& { return std::move(m_updates); } auto getDependencies() -> QList>&& { return std::move(m_deps); } public slots: @@ -59,14 +59,14 @@ class CheckUpdateTask : public Task { void executeTask() override = 0; signals: - void checkFailed(Mod* failed, QString reason, QUrl recover_url = {}); + void checkFailed(Resource* failed, QString reason, QUrl recover_url = {}); protected: - QList& m_mods; + QList& m_resources; std::list& m_game_versions; std::optional m_loaders; - std::shared_ptr m_mods_folder; + std::shared_ptr m_resource_model; - std::vector m_updatable; + std::vector m_updates; QList> m_deps; }; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index a6a76adb6..9a3249bc7 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -120,19 +120,19 @@ ModPlatform::IndexedVersion FlameCheckUpdate::getFileInfo(int addonId, int fileI * */ void FlameCheckUpdate::executeTask() { - setStatus(tr("Preparing mods for CurseForge...")); + setStatus(tr("Preparing resources for CurseForge...")); int i = 0; - for (auto* mod : m_mods) { - if (!mod->enabled()) { - emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); + for (auto* resource : m_resources) { + if (!resource->enabled()) { + emit checkFailed(resource, tr("Disabled resources won't be updated, to prevent resource duplication issues!")); continue; } - setStatus(tr("Getting API response from CurseForge for '%1'...").arg(mod->name())); - setProgress(i++, m_mods.size()); + setStatus(tr("Getting API response from CurseForge for '%1'...").arg(resource->name())); + setProgress(i++, m_resources.size()); - auto latest_ver = api.getLatestVersion({ { mod->metadata()->project_id.toString() }, m_game_versions, m_loaders }); + auto latest_ver = api.getLatestVersion({ { resource->metadata()->project_id.toString() }, m_game_versions, m_loaders }); // Check if we were aborted while getting the latest version if (m_was_aborted) { @@ -140,43 +140,50 @@ void FlameCheckUpdate::executeTask() return; } - setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(mod->name())); + setStatus(tr("Parsing the API response from CurseForge for '%1'...").arg(resource->name())); if (!latest_ver.addonId.isValid()) { - emit checkFailed(mod, tr("No valid version found for this mod. It's probably unavailable for the current game " - "version / mod loader.")); + QString reason; + if (dynamic_cast(resource) != nullptr) + reason = + tr("No valid version found for this resource. It's probably unavailable for the current game " + "version / mod loader."); + else + reason = tr("No valid version found for this resource. It's probably unavailable for the current game version."); + + emit checkFailed(resource, reason); continue; } - if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != mod->metadata()->file_id) { + if (latest_ver.downloadUrl.isEmpty() && latest_ver.fileId != resource->metadata()->file_id) { auto pack = getProjectInfo(latest_ver); auto recover_url = QString("%1/download/%2").arg(pack.websiteUrl, latest_ver.fileId.toString()); - emit checkFailed(mod, tr("Mod has a new update available, but is not downloadable using CurseForge."), recover_url); + emit checkFailed(resource, tr("Resource has a new update available, but is not downloadable using CurseForge."), recover_url); continue; } // Fake pack with the necessary info to pass to the download task :) auto pack = std::make_shared(); - pack->name = mod->name(); - pack->slug = mod->metadata()->slug; - pack->addonId = mod->metadata()->project_id; - pack->websiteUrl = mod->homeurl(); - for (auto& author : mod->authors()) - pack->authors.append({ author }); - pack->description = mod->description(); + pack->name = resource->name(); + pack->slug = resource->metadata()->slug; + pack->addonId = resource->metadata()->project_id; pack->provider = ModPlatform::ResourceProvider::FLAME; - if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ResourceStatus::NOT_INSTALLED)) { - auto old_version = mod->version(); - if (old_version.isEmpty() && mod->status() != ResourceStatus::NOT_INSTALLED) { - auto current_ver = getFileInfo(latest_ver.addonId.toInt(), mod->metadata()->file_id.toInt()); - old_version = current_ver.version; + if (!latest_ver.hash.isEmpty() && + (resource->metadata()->hash != latest_ver.hash || resource->status() == ResourceStatus::NOT_INSTALLED)) { + auto download_task = makeShared(pack, latest_ver, m_resource_model); + + QString old_version = resource->metadata()->version_number; + if (old_version.isEmpty()) { + if (resource->status() == ResourceStatus::NOT_INSTALLED) + old_version = tr("Not installed"); + else + old_version = tr("Unknown"); } - auto download_task = makeShared(pack, latest_ver, m_mods_folder); - m_updatable.emplace_back(pack->name, mod->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type, - api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), - ModPlatform::ResourceProvider::FLAME, download_task); + m_updates.emplace_back(pack->name, resource->metadata()->hash, old_version, latest_ver.version, latest_ver.version_type, + api.getModFileChangelog(latest_ver.addonId.toInt(), latest_ver.fileId.toInt()), + ModPlatform::ResourceProvider::FLAME, download_task); } m_deps.append(std::make_shared(pack, latest_ver)); } diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index f5bb1653d..9ae944153 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -8,11 +8,11 @@ class FlameCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - FlameCheckUpdate(QList& mods, + FlameCheckUpdate(QList& resources, std::list& mcVersions, std::optional loaders, - std::shared_ptr mods_folder) - : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) + std::shared_ptr resource_model) + : CheckUpdateTask(resources, mcVersions, loaders, resource_model) {} public slots: diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index 13696d8a2..f7e9fb8a9 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -29,38 +29,38 @@ bool ModrinthCheckUpdate::abort() * */ void ModrinthCheckUpdate::executeTask() { - setStatus(tr("Preparing mods for Modrinth...")); + setStatus(tr("Preparing resources for Modrinth...")); setProgress(0, 3); - QHash mappings; + QHash mappings; // Create all hashes QStringList hashes; auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); - for (auto* mod : m_mods) { - if (!mod->enabled()) { - emit checkFailed(mod, tr("Disabled mods won't be updated, to prevent mod duplication issues!")); + for (auto* resource : m_resources) { + if (!resource->enabled()) { + emit checkFailed(resource, tr("Disabled resources won't be updated, to prevent resource duplication issues!")); continue; } - auto hash = mod->metadata()->hash; + auto hash = resource->metadata()->hash; // Sadly the API can only handle one hash type per call, se we // need to generate a new hash if the current one is innadequate // (though it will rarely happen, if at all) - if (mod->metadata()->hash_format != best_hash_type) { - auto hash_task = Hashing::createModrinthHasher(mod->fileinfo().absoluteFilePath()); - connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, mod](QString hash) { + if (resource->metadata()->hash_format != best_hash_type) { + auto hash_task = Hashing::createModrinthHasher(resource->fileinfo().absoluteFilePath()); + connect(hash_task.get(), &Hashing::Hasher::resultsReady, [&hashes, &mappings, resource](QString hash) { hashes.append(hash); - mappings.insert(hash, mod); + mappings.insert(hash, resource); }); connect(hash_task.get(), &Task::failed, [this] { failed("Failed to generate hash"); }); hashing_task.addTask(hash_task); } else { hashes.append(hash); - mappings.insert(hash, mod); + mappings.insert(hash, resource); } } @@ -88,19 +88,26 @@ void ModrinthCheckUpdate::executeTask() setProgress(2, 3); try { - for (auto hash : mappings.keys()) { + for (auto iter = mappings.begin(); iter != mappings.end(); iter++) { + const QString& hash = iter.key(); + Resource* resource = iter.value(); auto project_obj = doc[hash].toObject(); // If the returned project is empty, but we have Modrinth metadata, // it means this specific version is not available if (project_obj.isEmpty()) { - qDebug() << "Mod " << mappings.find(hash).value()->name() << " got an empty response."; + qDebug() << "Resource " << resource->name() << " got an empty response."; qDebug() << "Hash: " << hash; - emit checkFailed( - mappings.find(hash).value(), - tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader.")); + QString reason; + if (dynamic_cast(resource) != nullptr) + reason = + tr("No valid version found for this resource. It's probably unavailable for the current game " + "version / mod loader."); + else + reason = tr("No valid version found for this resource. It's probably unavailable for the current game version."); + emit checkFailed(resource, reason); continue; } @@ -109,7 +116,8 @@ void ModrinthCheckUpdate::executeTask() QString loader_filter; if (m_loaders.has_value()) { static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge, - ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt }; + ModPlatform::ModLoaderType::Fabric, ModPlatform::ModLoaderType::Quilt, + ModPlatform::ModLoaderType::LiteLoader }; for (auto flag : flags) { if (m_loaders.value().testFlag(flag)) { loader_filter = ModPlatform::getModLoaderString(flag); @@ -126,46 +134,44 @@ void ModrinthCheckUpdate::executeTask() auto project_ver = Modrinth::loadIndexedPackVersion(project_obj, best_hash_type, loader_filter); if (project_ver.downloadUrl.isEmpty()) { - qCritical() << "Modrinth mod without download url!"; + qCritical() << "Modrinth resource without download url!"; qCritical() << project_ver.fileName; - emit checkFailed(mappings.find(hash).value(), tr("Mod has an empty download URL")); + emit checkFailed(mappings.find(hash).value(), tr("Resource has an empty download URL")); continue; } - auto mod_iter = mappings.find(hash); - if (mod_iter == mappings.end()) { - qCritical() << "Failed to remap mod from Modrinth!"; + auto resource_iter = mappings.find(hash); + if (resource_iter == mappings.end()) { + qCritical() << "Failed to remap resource from Modrinth!"; continue; } - auto mod = *mod_iter; - - auto key = project_ver.hash; // Fake pack with the necessary info to pass to the download task :) auto pack = std::make_shared(); - pack->name = mod->name(); - pack->slug = mod->metadata()->slug; - pack->addonId = mod->metadata()->project_id; - pack->websiteUrl = mod->homeurl(); - for (auto& author : mod->authors()) - pack->authors.append({ author }); - pack->description = mod->description(); + pack->name = resource->name(); + pack->slug = resource->metadata()->slug; + pack->addonId = resource->metadata()->project_id; pack->provider = ModPlatform::ResourceProvider::MODRINTH; - if ((key != hash && project_ver.is_preferred) || (mod->status() == ResourceStatus::NOT_INSTALLED)) { - if (mod->version() == project_ver.version_number) - continue; + if ((project_ver.hash != hash && project_ver.is_preferred) || (resource->status() == ResourceStatus::NOT_INSTALLED)) { + auto download_task = makeShared(pack, project_ver, m_resource_model); - auto download_task = makeShared(pack, project_ver, m_mods_folder); + QString old_version = resource->metadata()->version_number; + if (old_version.isEmpty()) { + if (resource->status() == ResourceStatus::NOT_INSTALLED) + old_version = tr("Not installed"); + else + old_version = tr("Unknown"); + } - m_updatable.emplace_back(pack->name, hash, mod->version(), project_ver.version_number, project_ver.version_type, - project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); + m_updates.emplace_back(pack->name, hash, old_version, project_ver.version_number, project_ver.version_type, + project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } m_deps.append(std::make_shared(pack, project_ver)); } } catch (Json::JsonException& e) { - emitFailed(e.cause() + " : " + e.what()); + emitFailed(e.cause() + ": " + e.what()); return; } emitSucceeded(); diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index f2f2c7e92..5aa3c8cb6 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -8,11 +8,11 @@ class ModrinthCheckUpdate : public CheckUpdateTask { Q_OBJECT public: - ModrinthCheckUpdate(QList& mods, + ModrinthCheckUpdate(QList& resources, std::list& mcVersions, std::optional loaders, - std::shared_ptr mods_folder) - : CheckUpdateTask(mods, mcVersions, loaders, mods_folder) + std::shared_ptr resource_model) + : CheckUpdateTask(resources, mcVersions, loaders, resource_model) {} public slots: diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index f68624c1a..81c0a1efb 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -297,18 +297,20 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { // [update] info using Provider = ModPlatform::ResourceProvider; - auto update_table = table["update"]; - if (!update_table || !update_table.is_table()) { + auto update_table = table["update"].as_table(); + if (!update_table) { qCritical() << QString("No [update] section found on mod metadata!"); return {}; } + mod.version_number = stringEntry(*update_table, "x-prismlauncher-version-number"); + toml::table* mod_provider_table = nullptr; - if ((mod_provider_table = update_table[ProviderCaps.name(Provider::FLAME)].as_table())) { + if ((mod_provider_table = (*update_table)[ProviderCaps.name(Provider::FLAME)].as_table())) { mod.provider = Provider::FLAME; mod.file_id = intEntry(*mod_provider_table, "file-id"); mod.project_id = intEntry(*mod_provider_table, "project-id"); - } else if ((mod_provider_table = update_table[ProviderCaps.name(Provider::MODRINTH)].as_table())) { + } else if ((mod_provider_table = (*update_table)[ProviderCaps.name(Provider::MODRINTH)].as_table())) { mod.provider = Provider::MODRINTH; mod.mod_id() = stringEntry(*mod_provider_table, "mod-id"); mod.version() = stringEntry(*mod_provider_table, "version"); diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ResourceUpdateDialog.cpp similarity index 74% rename from launcher/ui/dialogs/ModUpdateDialog.cpp rename to launcher/ui/dialogs/ResourceUpdateDialog.cpp index eef690d57..cad5b9f7f 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ResourceUpdateDialog.cpp @@ -1,4 +1,4 @@ -#include "ModUpdateDialog.h" +#include "ResourceUpdateDialog.h" #include "ChooseProviderDialog.h" #include "CustomMessageBox.h" #include "ProgressDialog.h" @@ -36,14 +36,14 @@ static std::optional mcLoaders(BaseInstance* inst) return { static_cast(inst)->getPackProfile()->getSupportedModLoaders() }; } -ModUpdateDialog::ModUpdateDialog(QWidget* parent, - BaseInstance* instance, - const std::shared_ptr mods, - QList& search_for, - bool includeDeps) +ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, + BaseInstance* instance, + const std::shared_ptr resource_model, + QList& search_for, + bool includeDeps) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) - , m_mod_model(mods) + , m_resource_model(resource_model) , m_candidates(search_for) , m_second_try_metadata( new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) @@ -56,7 +56,7 @@ ModUpdateDialog::ModUpdateDialog(QWidget* parent, ui->onlyCheckedLabel->setText(tr("Only mods with a check will be updated!")); } -void ModUpdateDialog::checkCandidates() +void ResourceUpdateDialog::checkCandidates() { // Ensure mods have valid metadata auto went_well = ensureMetadata(); @@ -92,18 +92,20 @@ void ModUpdateDialog::checkCandidates() SequentialTask check_task(m_parent, tr("Checking for updates")); if (!m_modrinth_to_update.empty()) { - m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_mod_model)); - connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { - m_failed_check_update.append({ mod, reason, recover_url }); - }); + m_modrinth_check_task.reset(new ModrinthCheckUpdate(m_modrinth_to_update, versions, loaders, m_resource_model)); + connect(m_modrinth_check_task.get(), &CheckUpdateTask::checkFailed, this, + [this](Resource* resource, QString reason, QUrl recover_url) { + m_failed_check_update.append({ resource, reason, recover_url }); + }); check_task.addTask(m_modrinth_check_task); } if (!m_flame_to_update.empty()) { - m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_mod_model)); - connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, [this](Mod* mod, QString reason, QUrl recover_url) { - m_failed_check_update.append({ mod, reason, recover_url }); - }); + m_flame_check_task.reset(new FlameCheckUpdate(m_flame_to_update, versions, loaders, m_resource_model)); + connect(m_flame_check_task.get(), &CheckUpdateTask::checkFailed, this, + [this](Resource* resource, QString reason, QUrl recover_url) { + m_failed_check_update.append({ resource, reason, recover_url }); + }); check_task.addTask(m_flame_check_task); } @@ -134,11 +136,11 @@ void ModUpdateDialog::checkCandidates() // Add found updates for Modrinth if (m_modrinth_check_task) { - auto modrinth_updates = m_modrinth_check_task->getUpdatable(); + auto modrinth_updates = m_modrinth_check_task->getUpdates(); for (auto& updatable : modrinth_updates) { qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); - appendMod(updatable); + appendResource(updatable); m_tasks.insert(updatable.name, updatable.download); } selectedVers.append(m_modrinth_check_task->getDependencies()); @@ -146,11 +148,11 @@ void ModUpdateDialog::checkCandidates() // Add found updated for Flame if (m_flame_check_task) { - auto flame_updates = m_flame_check_task->getUpdatable(); + auto flame_updates = m_flame_check_task->getUpdates(); for (auto& updatable : flame_updates) { qDebug() << QString("Mod %1 has an update available!").arg(updatable.name); - appendMod(updatable); + appendResource(updatable); m_tasks.insert(updatable.name, updatable.download); } selectedVers.append(m_flame_check_task->getDependencies()); @@ -189,49 +191,53 @@ void ModUpdateDialog::checkCandidates() } if (m_include_deps && !APPLICATION->settings()->get("ModDependenciesDisabled").toBool()) { // dependencies - auto depTask = makeShared(this, m_instance, m_mod_model.get(), selectedVers); + auto* mod_model = dynamic_cast(m_resource_model.get()); - connect(depTask.get(), &Task::failed, this, - [&](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + if (mod_model != nullptr) { + auto depTask = makeShared(this, m_instance, mod_model, selectedVers); - connect(depTask.get(), &Task::succeeded, this, [&]() { - QStringList warnings = depTask->warnings(); - if (warnings.count()) { - CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + connect(depTask.get(), &Task::failed, this, + [&](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + + connect(depTask.get(), &Task::succeeded, this, [&]() { + QStringList warnings = depTask->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->exec(); + } + }); + + ProgressDialog progress_dialog_deps(m_parent); + progress_dialog_deps.setSkipButton(true, tr("Abort")); + progress_dialog_deps.setWindowTitle(tr("Checking for dependencies...")); + auto dret = progress_dialog_deps.execWithTask(depTask.get()); + + // If the dialog was skipped / some download error happened + if (dret == QDialog::DialogCode::Rejected) { + m_aborted = true; + QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); + return; } - }); + static FlameAPI api; - ProgressDialog progress_dialog_deps(m_parent); - progress_dialog_deps.setSkipButton(true, tr("Abort")); - progress_dialog_deps.setWindowTitle(tr("Checking for dependencies...")); - auto dret = progress_dialog_deps.execWithTask(depTask.get()); + auto getRequiredBy = depTask->getRequiredBy(); - // If the dialog was skipped / some download error happened - if (dret == QDialog::DialogCode::Rejected) { - m_aborted = true; - QMetaObject::invokeMethod(this, "reject", Qt::QueuedConnection); - return; - } - static FlameAPI api; + for (const auto& dep : depTask->getDependecies()) { + auto changelog = dep->version.changelog; + if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) + changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); + auto download_task = makeShared(dep->pack, dep->version, m_resource_model); + CheckUpdateTask::Update updatable = { + dep->pack->name, dep->version.hash, tr("Not installed"), dep->version.version, dep->version.version_type, + changelog, dep->pack->provider, download_task + }; - auto getRequiredBy = depTask->getRequiredBy(); - - for (auto dep : depTask->getDependecies()) { - auto changelog = dep->version.changelog; - if (dep->pack->provider == ModPlatform::ResourceProvider::FLAME) - changelog = api.getModFileChangelog(dep->version.addonId.toInt(), dep->version.fileId.toInt()); - auto download_task = makeShared(dep->pack, dep->version, m_mod_model); - CheckUpdateTask::UpdatableMod updatable = { - dep->pack->name, dep->version.hash, "", dep->version.version, dep->version.version_type, - changelog, dep->pack->provider, download_task - }; - - appendMod(updatable, getRequiredBy.value(dep->version.addonId.toString())); - m_tasks.insert(updatable.name, updatable.download); + appendResource(updatable, getRequiredBy.value(dep->version.addonId.toString())); + m_tasks.insert(updatable.name, updatable.download); + } } } - // If there's no mod to be updated + // If there's no resource to be updated if (ui->modTreeWidget->topLevelItemCount() == 0) { m_no_updates = true; } else { @@ -253,7 +259,7 @@ void ModUpdateDialog::checkCandidates() } // Part 1: Ensure we have a valid metadata -auto ModUpdateDialog::ensureMetadata() -> bool +auto ResourceUpdateDialog::ensureMetadata() -> bool { auto index_dir = indexDir(); @@ -261,21 +267,21 @@ auto ModUpdateDialog::ensureMetadata() -> bool // A better use of data structures here could remove the need for this QHash QHash should_try_others; - QList modrinth_tmp; - QList flame_tmp; + QList modrinth_tmp; + QList flame_tmp; bool confirm_rest = false; bool try_others_rest = false; bool skip_rest = false; ModPlatform::ResourceProvider provider_rest = ModPlatform::ResourceProvider::MODRINTH; - auto addToTmp = [&](Mod* m, ModPlatform::ResourceProvider p) { + auto addToTmp = [&](Resource* resource, ModPlatform::ResourceProvider p) { switch (p) { case ModPlatform::ResourceProvider::MODRINTH: - modrinth_tmp.push_back(m); + modrinth_tmp.push_back(resource); break; case ModPlatform::ResourceProvider::FLAME: - flame_tmp.push_back(m); + flame_tmp.push_back(resource); break; } }; @@ -300,7 +306,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool } ChooseProviderDialog chooser(this); - chooser.setDescription(tr("The mod '%1' does not have a metadata yet. We need to generate it in order to track relevant " + chooser.setDescription(tr("The resource '%1' does not have a metadata yet. We need to generate it in order to track relevant " "information on how to update this mod. " "To do this, please select a mod provider which we can use to check for updates for this mod.") .arg(candidate->name())); @@ -324,8 +330,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool if (!modrinth_tmp.empty()) { auto modrinth_task = makeShared(modrinth_tmp, index_dir, ModPlatform::ResourceProvider::MODRINTH); - connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + connect(modrinth_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); + connect(modrinth_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::MODRINTH); }); connect(modrinth_task.get(), &EnsureMetadataTask::failed, @@ -339,8 +345,8 @@ auto ModUpdateDialog::ensureMetadata() -> bool if (!flame_tmp.empty()) { auto flame_task = makeShared(flame_tmp, index_dir, ModPlatform::ResourceProvider::FLAME); - connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Mod* candidate) { + connect(flame_task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); + connect(flame_task.get(), &EnsureMetadataTask::metadataFailed, [this, &should_try_others](Resource* candidate) { onMetadataFailed(candidate, should_try_others.find(candidate->internal_id()).value(), ModPlatform::ResourceProvider::FLAME); }); connect(flame_task.get(), &EnsureMetadataTask::failed, @@ -362,18 +368,18 @@ auto ModUpdateDialog::ensureMetadata() -> bool return (ret_metadata != QDialog::DialogCode::Rejected); } -void ModUpdateDialog::onMetadataEnsured(Mod* mod) +void ResourceUpdateDialog::onMetadataEnsured(Resource* resource) { // When the mod is a folder, for instance - if (!mod->metadata()) + if (!resource->metadata()) return; - switch (mod->metadata()->provider) { + switch (resource->metadata()->provider) { case ModPlatform::ResourceProvider::MODRINTH: - m_modrinth_to_update.push_back(mod); + m_modrinth_to_update.push_back(resource); break; case ModPlatform::ResourceProvider::FLAME: - m_flame_to_update.push_back(mod); + m_flame_to_update.push_back(resource); break; } } @@ -390,26 +396,26 @@ ModPlatform::ResourceProvider next(ModPlatform::ResourceProvider p) return ModPlatform::ResourceProvider::FLAME; } -void ModUpdateDialog::onMetadataFailed(Mod* mod, bool try_others, ModPlatform::ResourceProvider first_choice) +void ResourceUpdateDialog::onMetadataFailed(Resource* resource, bool try_others, ModPlatform::ResourceProvider first_choice) { if (try_others) { auto index_dir = indexDir(); - auto task = makeShared(mod, index_dir, next(first_choice)); - connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Mod* candidate) { onMetadataEnsured(candidate); }); - connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Mod* candidate) { onMetadataFailed(candidate, false); }); + auto task = makeShared(resource, index_dir, next(first_choice)); + connect(task.get(), &EnsureMetadataTask::metadataReady, [this](Resource* candidate) { onMetadataEnsured(candidate); }); + connect(task.get(), &EnsureMetadataTask::metadataFailed, [this](Resource* candidate) { onMetadataFailed(candidate, false); }); connect(task.get(), &EnsureMetadataTask::failed, - [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); + [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->exec(); }); m_second_try_metadata->addTask(task); } else { QString reason{ tr("Couldn't find a valid version on the selected mod provider(s)") }; - m_failed_metadata.append({ mod, reason }); + m_failed_metadata.append({ resource, reason }); } } -void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStringList requiredBy) +void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, QStringList requiredBy) { auto item_top = new QTreeWidgetItem(ui->modTreeWidget); item_top->setCheckState(0, Qt::CheckState::Checked); @@ -420,7 +426,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider))); auto old_version_item = new QTreeWidgetItem(item_top); - old_version_item->setText(0, tr("Old version: %1").arg(info.old_version.isEmpty() ? tr("Not installed") : info.old_version)); + old_version_item->setText(0, tr("Old version: %1").arg(info.old_version)); auto new_version_item = new QTreeWidgetItem(item_top); new_version_item->setText(0, tr("New version: %1").arg(info.new_version)); @@ -474,7 +480,7 @@ void ModUpdateDialog::appendMod(CheckUpdateTask::UpdatableMod const& info, QStri ui->modTreeWidget->addTopLevelItem(item_top); } -auto ModUpdateDialog::getTasks() -> const QList +auto ResourceUpdateDialog::getTasks() -> const QList { QList list; diff --git a/launcher/ui/dialogs/ModUpdateDialog.h b/launcher/ui/dialogs/ResourceUpdateDialog.h similarity index 53% rename from launcher/ui/dialogs/ModUpdateDialog.h rename to launcher/ui/dialogs/ResourceUpdateDialog.h index de5ab46a5..31aab8f5d 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.h +++ b/launcher/ui/dialogs/ResourceUpdateDialog.h @@ -13,22 +13,21 @@ class ModrinthCheckUpdate; class FlameCheckUpdate; class ConcurrentTask; -class ModUpdateDialog final : public ReviewMessageBox { +class ResourceUpdateDialog final : public ReviewMessageBox { Q_OBJECT public: - explicit ModUpdateDialog(QWidget* parent, BaseInstance* instance, std::shared_ptr mod_model, QList& search_for); - explicit ModUpdateDialog(QWidget* parent, - BaseInstance* instance, - std::shared_ptr mod_model, - QList& search_for, - bool includeDeps); + explicit ResourceUpdateDialog(QWidget* parent, + BaseInstance* instance, + std::shared_ptr resource_model, + QList& search_for, + bool includeDeps); void checkCandidates(); - void appendMod(const CheckUpdateTask::UpdatableMod& info, QStringList requiredBy = {}); + void appendResource(const CheckUpdateTask::Update& info, QStringList requiredBy = {}); const QList getTasks(); - auto indexDir() const -> QDir { return m_mod_model->indexDir(); } + auto indexDir() const -> QDir { return m_resource_model->indexDir(); } auto noUpdates() const -> bool { return m_no_updates; }; auto aborted() const -> bool { return m_aborted; }; @@ -37,8 +36,8 @@ class ModUpdateDialog final : public ReviewMessageBox { auto ensureMetadata() -> bool; private slots: - void onMetadataEnsured(Mod*); - void onMetadataFailed(Mod*, + void onMetadataEnsured(Resource* resource); + void onMetadataFailed(Resource* resource, bool try_others = false, ModPlatform::ResourceProvider first_choice = ModPlatform::ResourceProvider::MODRINTH); @@ -48,15 +47,15 @@ class ModUpdateDialog final : public ReviewMessageBox { shared_qobject_ptr m_modrinth_check_task; shared_qobject_ptr m_flame_check_task; - const std::shared_ptr m_mod_model; + const std::shared_ptr m_resource_model; - QList& m_candidates; - QList m_modrinth_to_update; - QList m_flame_to_update; + QList& m_candidates; + QList m_modrinth_to_update; + QList m_flame_to_update; ConcurrentTask::Ptr m_second_try_metadata; - QList> m_failed_metadata; - QList> m_failed_check_update; + QList> m_failed_metadata; + QList> m_failed_check_update; QHash m_tasks; BaseInstance* m_instance; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 0cc60205d..e513cb1fa 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -51,8 +51,8 @@ #include "ui/GuiUtil.h" #include "ui/dialogs/CustomMessageBox.h" -#include "ui/dialogs/ModUpdateDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" #include "DesktopServices.h" @@ -161,9 +161,8 @@ bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unuse { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); - Mod const* m = m_model->at(row); - if (m) - ui->frame->updateWithMod(*m); + const Mod& mod = m_model->at(row); + ui->frame->updateWithMod(mod); return true; } @@ -253,12 +252,12 @@ void ModFolderPage::updateMods(bool includeDeps) } auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto mods_list = m_model->selectedMods(selection); + auto mods_list = m_model->selectedResources(selection); bool use_all = mods_list.empty(); if (use_all) - mods_list = m_model->allMods(); + mods_list = m_model->allResources(); - ModUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps); + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps); update_dialog.checkCandidates(); if (update_dialog.aborted()) {