From 93876e27f80a63b36c2cce73b014bf031054bb99 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 31 Aug 2023 17:32:55 +0100 Subject: [PATCH 01/39] Generalise resource metadata Signed-off-by: TheKodeToad --- launcher/CMakeLists.txt | 5 +- launcher/QObjectPtr.h | 6 +- launcher/minecraft/MinecraftInstance.cpp | 13 ++-- launcher/minecraft/mod/MetadataHandler.h | 51 ++++++++----- launcher/minecraft/mod/Mod.cpp | 73 +----------------- launcher/minecraft/mod/Mod.h | 15 +--- launcher/minecraft/mod/ModDetails.h | 48 +----------- launcher/minecraft/mod/ModFolderModel.cpp | 46 ++---------- launcher/minecraft/mod/ModFolderModel.h | 14 +--- launcher/minecraft/mod/Resource.cpp | 45 ++++++++++- launcher/minecraft/mod/Resource.h | 28 ++++++- .../minecraft/mod/ResourceFolderModel.cpp | 14 ++-- launcher/minecraft/mod/ResourceFolderModel.h | 11 ++- .../minecraft/mod/ResourcePackFolderModel.cpp | 10 +-- .../minecraft/mod/ResourcePackFolderModel.h | 6 +- .../minecraft/mod/ShaderPackFolderModel.h | 4 +- .../minecraft/mod/TexturePackFolderModel.cpp | 10 +-- .../minecraft/mod/TexturePackFolderModel.h | 5 +- .../minecraft/mod/tasks/BasicFolderLoadTask.h | 74 ------------------- .../minecraft/mod/tasks/LocalModParseTask.h | 5 -- ...oadTask.cpp => ResourceFolderLoadTask.cpp} | 71 +++++++++--------- ...derLoadTask.h => ResourceFolderLoadTask.h} | 12 ++- launcher/modplatform/EnsureMetadataTask.cpp | 2 +- .../modplatform/flame/FlameCheckUpdate.cpp | 4 +- .../modrinth/ModrinthCheckUpdate.cpp | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 2 +- launcher/modplatform/packwiz/Packwiz.h | 1 + launcher/ui/dialogs/ModUpdateDialog.cpp | 2 +- 28 files changed, 209 insertions(+), 370 deletions(-) delete mode 100644 launcher/minecraft/mod/tasks/BasicFolderLoadTask.h rename launcher/minecraft/mod/tasks/{ModFolderLoadTask.cpp => ResourceFolderLoadTask.cpp} (55%) rename launcher/minecraft/mod/tasks/{ModFolderLoadTask.h => ResourceFolderLoadTask.h} (84%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 18e0acab1..cd4c64dec 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -350,9 +350,8 @@ set(MINECRAFT_SOURCES minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.cpp minecraft/mod/ShaderPackFolderModel.h - minecraft/mod/tasks/BasicFolderLoadTask.h - minecraft/mod/tasks/ModFolderLoadTask.h - minecraft/mod/tasks/ModFolderLoadTask.cpp + minecraft/mod/tasks/ResourceFolderLoadTask.h + minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.cpp minecraft/mod/tasks/LocalModUpdateTask.h diff --git a/launcher/QObjectPtr.h b/launcher/QObjectPtr.h index a1c64b433..5f7b64df0 100644 --- a/launcher/QObjectPtr.h +++ b/launcher/QObjectPtr.h @@ -33,11 +33,7 @@ class shared_qobject_ptr : public QSharedPointer { {} void reset() { QSharedPointer::reset(); } - void reset(T*&& other) - { - shared_qobject_ptr t(other); - this->swap(t); - } + void reset(T* other) { QSharedPointer::reset(other); } void reset(const shared_qobject_ptr& other) { shared_qobject_ptr t(other); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 0e64c46d4..a75d017b7 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1091,7 +1091,7 @@ std::shared_ptr MinecraftInstance::loaderModList() { if (!m_loader_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed)); + m_loader_mod_list.reset(new ModFolderModel(modsRoot(), this, is_indexed, true)); } return m_loader_mod_list; } @@ -1100,7 +1100,7 @@ std::shared_ptr MinecraftInstance::coreModList() { if (!m_core_mod_list) { bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); - m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed)); + m_core_mod_list.reset(new ModFolderModel(coreModsDir(), this, is_indexed, true)); } return m_core_mod_list; } @@ -1117,7 +1117,8 @@ std::shared_ptr MinecraftInstance::nilModList() std::shared_ptr MinecraftInstance::resourcePackList() { if (!m_resource_pack_list) { - m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this)); + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_resource_pack_list.reset(new ResourcePackFolderModel(resourcePacksDir(), this, is_indexed, true)); } return m_resource_pack_list; } @@ -1125,7 +1126,8 @@ std::shared_ptr MinecraftInstance::resourcePackList() std::shared_ptr MinecraftInstance::texturePackList() { if (!m_texture_pack_list) { - m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this)); + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_texture_pack_list.reset(new TexturePackFolderModel(texturePacksDir(), this, is_indexed, true)); } return m_texture_pack_list; } @@ -1133,7 +1135,8 @@ std::shared_ptr MinecraftInstance::texturePackList() std::shared_ptr MinecraftInstance::shaderPackList() { if (!m_shader_pack_list) { - m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this)); + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_shader_pack_list.reset(new ShaderPackFolderModel(shaderPacksDir(), this, is_indexed, true)); } return m_shader_pack_list; } diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 88e9ff2b6..747375d3f 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -25,30 +25,41 @@ // launcher/minecraft/mod/Mod.h class Mod; -/* Abstraction file for easily changing the way metadata is stored / handled - * Needs to be a class because of -Wunused-function and no C++17 [[maybe_unused]] - * */ -class Metadata { - public: - using ModStruct = Packwiz::V1::Mod; +namespace Metadata { +using ModStruct = Packwiz::V1::Mod; - static auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct - { - return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); - } +inline auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct +{ + return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); +} - static auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct - { - return Packwiz::V1::createModFormat(index_dir, internal_mod, mod_slug); - } +inline auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct +{ + return Packwiz::V1::createModFormat(index_dir, internal_mod, std::move(mod_slug)); +} - static void update(QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } +inline void update(QDir& index_dir, ModStruct& mod) +{ + Packwiz::V1::updateModIndex(index_dir, mod); +} - static void remove(QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } +inline void remove(QDir& index_dir, QString mod_slug) +{ + Packwiz::V1::deleteModIndex(index_dir, mod_slug); +} - static void remove(QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); } +inline void remove(QDir& index_dir, QVariant& mod_id) +{ + Packwiz::V1::deleteModIndex(index_dir, mod_id); +} - static auto get(QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_slug); } +inline auto get(QDir& index_dir, QString mod_slug) -> ModStruct +{ + return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug)); +} - static auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } -}; +inline auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct +{ + return Packwiz::V1::getIndexForMod(index_dir, mod_id); +} +}; // namespace Metadata diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index ae3dea8d8..53f7d72de 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -43,35 +43,16 @@ #include "MTPixmapCache.h" #include "MetadataHandler.h" +#include "Resource.h" #include "Version.h" #include "minecraft/mod/ModDetails.h" #include "minecraft/mod/tasks/LocalModParseTask.h" -static ModPlatform::ProviderCapabilities ProviderCaps; - Mod::Mod(const QFileInfo& file) : Resource(file), m_local_details() { m_enabled = (file.suffix() != "disabled"); } -Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) : Mod(mods_dir.absoluteFilePath(metadata.filename)) -{ - m_name = metadata.name; - m_local_details.metadata = std::make_shared(std::move(metadata)); -} - -void Mod::setStatus(ModStatus status) -{ - m_local_details.status = status; -} -void Mod::setMetadata(std::shared_ptr&& metadata) -{ - if (status() == ModStatus::NoMetadata) - setStatus(ModStatus::Installed); - - m_local_details.metadata = metadata; -} - void Mod::setDetails(const ModDetails& details) { m_local_details = details; @@ -103,8 +84,7 @@ std::pair Mod::compare(const Resource& other, SortType type) const break; } case SortType::PROVIDER: { - auto compare_result = - QString::compare(provider().value_or("Unknown"), cast_other->provider().value_or("Unknown"), Qt::CaseInsensitive); + auto compare_result = QString::compare(provider(), cast_other->provider(), Qt::CaseInsensitive); if (compare_result != 0) return { compare_result, type == SortType::PROVIDER }; break; @@ -127,22 +107,6 @@ bool Mod::applyFilter(QRegularExpression filter) const return Resource::applyFilter(filter); } -auto Mod::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool -{ - if (!preserve_metadata) { - qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); - - if (metadata()) { - Metadata::remove(index_dir, metadata()->slug); - } else { - auto n = name(); - Metadata::remove(index_dir, n); - } - } - - return Resource::destroy(attempt_trash); -} - auto Mod::details() const -> const ModDetails& { return m_local_details; @@ -154,10 +118,7 @@ auto Mod::name() const -> QString if (!d_name.isEmpty()) return d_name; - if (metadata()) - return metadata()->name; - - return m_name; + return Resource::name(); } auto Mod::version() const -> QString @@ -187,45 +148,17 @@ auto Mod::authors() const -> QStringList return details().authors; } -auto Mod::status() const -> ModStatus -{ - return details().status; -} - -auto Mod::metadata() -> std::shared_ptr -{ - return m_local_details.metadata; -} - -auto Mod::metadata() const -> const std::shared_ptr -{ - return m_local_details.metadata; -} - void Mod::finishResolvingWithDetails(ModDetails&& details) { m_is_resolving = false; m_is_resolved = true; - std::shared_ptr metadata = details.metadata; - if (details.status == ModStatus::Unknown) - details.status = m_local_details.status; - m_local_details = std::move(details); - if (metadata) - setMetadata(std::move(metadata)); if (!iconPath().isEmpty()) { m_pack_image_cache_key.was_read_attempt = false; } } -auto Mod::provider() const -> std::optional -{ - if (metadata()) - return ProviderCaps.readableName(metadata()->provider); - return {}; -} - auto Mod::licenses() const -> const QList& { return details().licenses; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 6dafecfc5..5f82572a2 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -56,7 +56,6 @@ class Mod : public Resource { Mod() = default; Mod(const QFileInfo& file); - Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata); Mod(QString file_path) : Mod(QFileInfo(file_path)) {} auto details() const -> const ModDetails&; @@ -65,11 +64,10 @@ class Mod : public Resource { auto homeurl() const -> QString; auto description() const -> QString; auto authors() const -> QStringList; - auto status() const -> ModStatus; - auto provider() const -> std::optional; auto licenses() const -> const QList&; auto issueTracker() const -> QString; auto metaurl() const -> QString; + void setDetails(const ModDetails& details); /** Get the intneral path to the mod's icon file*/ QString iconPath() const { return m_local_details.icon_file; } @@ -78,22 +76,11 @@ class Mod : public Resource { /** Thread-safe. */ void setIcon(QImage new_image) const; - auto metadata() -> std::shared_ptr; - auto metadata() const -> const std::shared_ptr; - - void setStatus(ModStatus status); - void setMetadata(std::shared_ptr&& metadata); - void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared(metadata)); } - void setDetails(const ModDetails& details); - bool valid() const override; [[nodiscard]] auto compare(Resource const& other, SortType type) const -> std::pair override; [[nodiscard]] bool applyFilter(QRegularExpression filter) const override; - // Delete all the files of this mod - auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; - void finishResolvingWithDetails(ModDetails&& details); protected: diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index a00d5a24b..9195c0368 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -43,13 +43,6 @@ #include "minecraft/mod/MetadataHandler.h" -enum class ModStatus { - Installed, // Both JAR and Metadata are present - NotInstalled, // Only the Metadata is present - NoMetadata, // Only the JAR is present - Unknown, // Default status -}; - struct ModLicense { QString name = {}; QString id = {}; @@ -149,12 +142,6 @@ struct ModDetails { /* Path of mod logo */ QString icon_file = {}; - /* Installation status of the mod */ - ModStatus status = ModStatus::Unknown; - - /* Metadata information, if any */ - std::shared_ptr metadata = nullptr; - ModDetails() = default; /** Metadata should be handled manually to properly set the mod status. */ @@ -169,40 +156,9 @@ struct ModDetails { , issue_tracker(other.issue_tracker) , licenses(other.licenses) , icon_file(other.icon_file) - , status(other.status) {} - ModDetails& operator=(const ModDetails& other) - { - this->mod_id = other.mod_id; - this->name = other.name; - this->version = other.version; - this->mcversion = other.mcversion; - this->homeurl = other.homeurl; - this->description = other.description; - this->authors = other.authors; - this->issue_tracker = other.issue_tracker; - this->licenses = other.licenses; - this->icon_file = other.icon_file; - this->status = other.status; + ModDetails& operator=(const ModDetails& other) = default; - return *this; - } - - ModDetails& operator=(const ModDetails&& other) - { - this->mod_id = other.mod_id; - this->name = other.name; - this->version = other.version; - this->mcversion = other.mcversion; - this->homeurl = other.homeurl; - this->description = other.description; - this->authors = other.authors; - this->issue_tracker = other.issue_tracker; - this->licenses = other.licenses; - this->icon_file = other.icon_file; - this->status = other.status; - - return *this; - } + ModDetails& operator=(ModDetails&& other) = default; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index eed35615c..2f0e9b3e4 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -52,15 +52,16 @@ #include "Application.h" #include "Json.h" +#include "Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" -#include "minecraft/mod/tasks/ModFolderLoadTask.h" +#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" #include "modplatform/ModIndex.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" -ModFolderModel::ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed, bool create_dir) - : ResourceFolderModel(QDir(dir), instance, nullptr, create_dir), m_is_indexed(is_indexed) +ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) + : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Version", "Last Modified", "Provider" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Version"), tr("Last Modified"), tr("Provider") }); @@ -96,15 +97,8 @@ QVariant ModFolderModel::data(const QModelIndex& index, int role) const } case DateColumn: return m_resources[row]->dateTimeChanged(); - case ProviderColumn: { - auto provider = at(row)->provider(); - if (!provider.has_value()) { - //: Unknown mod provider (i.e. not Modrinth, CurseForge, etc...) - return tr("Unknown"); - } - - return provider.value(); - } + case ProviderColumn: + return at(row)->provider(); default: return QVariant(); } @@ -185,14 +179,6 @@ int ModFolderModel::columnCount(const QModelIndex& parent) const return parent.isValid() ? 0 : NUM_COLUMNS; } -Task* ModFolderModel::createUpdateTask() -{ - auto index_dir = indexDir(); - auto task = new ModFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load); - m_first_folder_load = false; - return task; -} - Task* ModFolderModel::createParseTask(Resource& resource) { return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo()); @@ -273,26 +259,6 @@ auto ModFolderModel::allMods() -> QList return mods; } -void ModFolderModel::onUpdateSucceeded() -{ - auto update_results = static_cast(m_current_update_task.get())->result(); - - auto& new_mods = update_results->mods; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) - auto current_list = m_resources_index.keys(); - QSet current_set(current_list.begin(), current_list.end()); - - auto new_list = new_mods.keys(); - QSet new_set(new_list.begin(), new_list.end()); -#else - QSet current_set(m_resources_index.keys().toSet()); - QSet new_set(new_mods.keys().toSet()); -#endif - - applyUpdates(current_set, new_set, new_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 f1890e87e..9326158d4 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -47,10 +47,9 @@ #include "ResourceFolderModel.h" #include "minecraft/mod/tasks/LocalModParseTask.h" -#include "minecraft/mod/tasks/ModFolderLoadTask.h" +#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" #include "modplatform/ModIndex.h" -class LegacyInstance; class BaseInstance; class QFileSystemWatcher; @@ -63,7 +62,7 @@ class ModFolderModel : public ResourceFolderModel { public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, VersionColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; enum ModStatusAction { Disable, Enable, Toggle }; - ModFolderModel(const QString& dir, BaseInstance* instance, bool is_indexed = false, bool create_dir = true); + ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "mods"; } @@ -72,7 +71,7 @@ class ModFolderModel : public ResourceFolderModel { QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; int columnCount(const QModelIndex& parent) const override; - [[nodiscard]] Task* createUpdateTask() override; + [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); } [[nodiscard]] Task* createParseTask(Resource&) override; bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } @@ -87,18 +86,11 @@ class ModFolderModel : public ResourceFolderModel { bool startWatching() override; bool stopWatching() override; - QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } - auto selectedMods(QModelIndexList& indexes) -> QList; auto allMods() -> QList; RESOURCE_HELPERS(Mod) private slots: - void onUpdateSucceeded() override; void onParseSucceeded(int ticket, QString resource_id) override; - - protected: - bool m_is_indexed; - bool m_first_folder_load = true; }; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index da806f0f4..0dd7f8992 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -5,6 +5,8 @@ #include "FileSystem.h" +static ModPlatform::ProviderCapabilities ProviderCaps; + Resource::Resource(QObject* parent) : QObject(parent) {} Resource::Resource(QFileInfo file_info) : QObject() @@ -54,6 +56,14 @@ void Resource::parseFile() m_changed_date_time = m_file_info.lastModified(); } +auto Resource::name() const -> QString +{ + if (metadata()) + return metadata()->name; + + return m_name; +} + static void removeThePrefix(QString& string) { QRegularExpression regex(QStringLiteral("^(?:the|teh) +"), QRegularExpression::CaseInsensitiveOption); @@ -61,6 +71,22 @@ static void removeThePrefix(QString& string) string = string.trimmed(); } +auto Resource::provider() const -> QString +{ + if (metadata()) + return ProviderCaps.readableName(metadata()->provider); + + return tr("Unknown"); +} + +void Resource::setMetadata(std::shared_ptr&& metadata) +{ + if (status() == ResourceStatus::NO_METADATA) + setStatus(ResourceStatus::INSTALLED); + + m_metadata = metadata; +} + std::pair Resource::compare(const Resource& other, SortType type) const { switch (type) { @@ -152,6 +178,23 @@ bool Resource::destroy(bool attemptTrash) return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } + +auto Resource::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool +{ + if (!preserve_metadata) { + qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); + + if (metadata()) { + Metadata::remove(index_dir, metadata()->slug); + } else { + auto n = name(); + Metadata::remove(index_dir, n); + } + } + + return destroy(attempt_trash); +} + bool Resource::isSymLinkUnder(const QString& instPath) const { if (isSymLink()) @@ -168,4 +211,4 @@ bool Resource::isSymLinkUnder(const QString& instPath) const bool Resource::isMoreThanOneHardLink() const { return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; -} +} \ No newline at end of file diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index c1ed49461..5012c23a4 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -5,6 +5,8 @@ #include #include +#include "MetadataHandler.h" +#include "ModDetails.h" #include "QObjectPtr.h" enum class ResourceType { @@ -15,6 +17,13 @@ enum class ResourceType { LITEMOD, //!< The resource is a litemod }; +enum class ResourceStatus { + INSTALLED, // Both JAR and Metadata are present + NOT_INSTALLED, // Only the Metadata is present + NO_METADATA, // Only the JAR is present + UNKNOWN, // Default status +}; + enum class SortType { NAME, DATE, VERSION, ENABLED, PACK_FORMAT, PROVIDER }; enum class EnableAction { ENABLE, DISABLE, TOGGLE }; @@ -46,9 +55,18 @@ class Resource : public QObject { [[nodiscard]] auto type() const -> ResourceType { return m_type; } [[nodiscard]] bool enabled() const { return m_enabled; } - [[nodiscard]] virtual auto name() const -> QString { return m_name; } + [[nodiscard]] virtual auto name() const -> QString; [[nodiscard]] virtual bool valid() const { return m_type != ResourceType::UNKNOWN; } + [[nodiscard]] auto status() const -> ResourceStatus { return m_status; }; + [[nodiscard]] auto metadata() -> std::shared_ptr { return m_metadata; } + [[nodiscard]] auto metadata() const -> std::shared_ptr { return m_metadata; } + [[nodiscard]] auto provider() const -> QString; + + void setStatus(ResourceStatus status) { m_status = status; } + void setMetadata(std::shared_ptr&& metadata); + void setMetadata(const Metadata::ModStruct& metadata) { setMetadata(std::make_shared(metadata)); } + /** Compares two Resources, for sorting purposes, considering a ascending order, returning: * > 0: 'this' comes after 'other' * = 0: 'this' is equal to 'other' @@ -81,7 +99,8 @@ class Resource : public QObject { } // Delete all files of this resource. - bool destroy(bool attemptTrash = true); + auto destroy(bool attemptTrash = true) -> bool; + auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; [[nodiscard]] auto isSymLink() const -> bool { return m_file_info.isSymLink(); } @@ -110,6 +129,11 @@ class Resource : public QObject { /* The type of file we're dealing with. */ ResourceType m_type = ResourceType::UNKNOWN; + /* Installation status of the resource. */ + ResourceStatus m_status = ResourceStatus::UNKNOWN; + + std::shared_ptr m_metadata = nullptr; + /* Whether the resource is enabled (e.g. shows up in the game) or not. */ bool m_enabled = true; diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index d3237b34b..a39e9adee 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -15,14 +15,14 @@ #include "FileSystem.h" #include "QVariantUtils.h" -#include "minecraft/mod/tasks/BasicFolderLoadTask.h" +#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" #include "settings/Setting.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" -ResourceFolderModel::ResourceFolderModel(QDir dir, BaseInstance* instance, QObject* parent, bool create_dir) - : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this) +ResourceFolderModel::ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) + : QAbstractListModel(parent), m_dir(dir), m_instance(instance), m_watcher(this), m_is_indexed(is_indexed) { if (create_dir) { FS::ensureFolderPathExists(m_dir.absolutePath()); @@ -290,7 +290,7 @@ void ResourceFolderModel::resolveResource(Resource* res) void ResourceFolderModel::onUpdateSucceeded() { - auto update_results = static_cast(m_current_update_task.get())->result(); + auto update_results = static_cast(m_current_update_task.get())->result(); auto& new_resources = update_results->resources; @@ -320,7 +320,11 @@ void ResourceFolderModel::onParseSucceeded(int ticket, QString resource_id) Task* ResourceFolderModel::createUpdateTask() { - return new BasicFolderLoadTask(m_dir); + auto index_dir = indexDir(); + auto task = new ResourceFolderLoadTask(dir(), index_dir, m_is_indexed, m_first_folder_load, + [this](const QFileInfo& file) { return createResource(file); }); + m_first_folder_load = false; + return task; } bool ResourceFolderModel::hasPendingParseTasks() const diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 80c31e456..3a4287b30 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -29,7 +29,7 @@ class QSortFilterProxyModel; class ResourceFolderModel : public QAbstractListModel { Q_OBJECT public: - ResourceFolderModel(QDir, BaseInstance* instance, QObject* parent = nullptr, bool create_dir = true); + ResourceFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); ~ResourceFolderModel() override; virtual QString id() const { return "resource"; } @@ -52,6 +52,8 @@ class ResourceFolderModel : public QAbstractListModel { virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } + QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } + /** Given a path in the system, install that resource, moving it to its place in the * instance file hierarchy. * @@ -152,7 +154,9 @@ class ResourceFolderModel : public QAbstractListModel { * This Task is normally executed when opening a page, so it shouldn't contain much heavy work. * If such work is needed, try using it in the Task create by createParseTask() instead! */ - [[nodiscard]] virtual Task* createUpdateTask(); + [[nodiscard]] Task* createUpdateTask(); + + [[nodiscard]] virtual Resource* createResource(const QFileInfo& info) { return new Resource(info); } /** This creates a new parse task to be executed by onUpdateSucceeded(). * @@ -210,6 +214,9 @@ class ResourceFolderModel : public QAbstractListModel { QFileSystemWatcher m_watcher; bool m_is_watching = false; + bool m_is_indexed; + bool m_first_folder_load = true; + Task::Ptr m_current_update_task = nullptr; bool m_scheduled_update = false; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index f27431576..2e72e02f2 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -44,10 +44,11 @@ #include "Application.h" #include "Version.h" -#include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalResourcePackParseTask.h" +#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" -ResourcePackFolderModel::ResourcePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) +ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) + : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); @@ -173,11 +174,6 @@ int ResourcePackFolderModel::columnCount(const QModelIndex& parent) const return parent.isValid() ? 0 : NUM_COLUMNS; } -Task* ResourcePackFolderModel::createUpdateTask() -{ - return new BasicFolderLoadTask(m_dir, [](QFileInfo const& entry) { return makeShared(entry); }); -} - Task* ResourcePackFolderModel::createParseTask(Resource& resource) { return new LocalResourcePackParseTask(m_next_resolution_ticket, static_cast(resource)); diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index 29c2c5995..fd56c8c73 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -9,16 +9,16 @@ class ResourcePackFolderModel : public ResourceFolderModel { public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; - explicit ResourcePackFolderModel(const QString& dir, BaseInstance* instance); + explicit ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); - virtual QString id() const override { return "resourcepacks"; } + QString id() const override { return "resourcepacks"; } [[nodiscard]] QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] int columnCount(const QModelIndex& parent) const override; - [[nodiscard]] Task* createUpdateTask() override; + [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new ResourcePack(file); } [[nodiscard]] Task* createParseTask(Resource&) override; RESOURCE_HELPERS(ResourcePack) diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index 44ed37a47..02faee49c 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -6,7 +6,9 @@ class ShaderPackFolderModel : public ResourceFolderModel { Q_OBJECT public: - explicit ShaderPackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) {} + explicit ShaderPackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr) + : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) + {} virtual QString id() const override { return "shaderpacks"; } }; diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 5c5f2b7c1..23a392421 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -39,10 +39,11 @@ #include "TexturePackFolderModel.h" -#include "minecraft/mod/tasks/BasicFolderLoadTask.h" #include "minecraft/mod/tasks/LocalTexturePackParseTask.h" +#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" -TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* instance) : ResourceFolderModel(QDir(dir), instance) +TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) + : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); @@ -52,11 +53,6 @@ TexturePackFolderModel::TexturePackFolderModel(const QString& dir, BaseInstance* m_columnsHideable = { false, true, false, 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)); diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index b975d8641..44b19b57a 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -46,7 +46,7 @@ class TexturePackFolderModel : public ResourceFolderModel { public: enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; - explicit TexturePackFolderModel(const QString& dir, std::shared_ptr instance); + explicit TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "texturepacks"; } @@ -55,8 +55,7 @@ class TexturePackFolderModel : public ResourceFolderModel { [[nodiscard]] QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; [[nodiscard]] int columnCount(const QModelIndex& parent) const override; - explicit TexturePackFolderModel(const QString& dir, BaseInstance* instance); - [[nodiscard]] Task* createUpdateTask() override; + [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new TexturePack(file); } [[nodiscard]] Task* createParseTask(Resource&) override; RESOURCE_HELPERS(TexturePack) diff --git a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h b/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h deleted file mode 100644 index 23a2b649a..000000000 --- a/launcher/minecraft/mod/tasks/BasicFolderLoadTask.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include - -#include "minecraft/mod/Resource.h" - -#include "tasks/Task.h" - -/** Very simple task that just loads a folder's contents directly. - */ -class BasicFolderLoadTask : public Task { - Q_OBJECT - public: - struct Result { - QMap resources; - }; - using ResultPtr = std::shared_ptr; - - [[nodiscard]] ResultPtr result() const { return m_result; } - - public: - BasicFolderLoadTask(QDir dir) : Task(nullptr, false), m_dir(dir), m_result(new Result), m_thread_to_spawn_into(thread()) - { - m_create_func = [](QFileInfo const& entry) -> Resource::Ptr { return makeShared(entry); }; - } - BasicFolderLoadTask(QDir dir, std::function create_function) - : Task(nullptr, false) - , m_dir(dir) - , m_result(new Result) - , m_create_func(std::move(create_function)) - , m_thread_to_spawn_into(thread()) - {} - - [[nodiscard]] bool canAbort() const override { return true; } - bool abort() override - { - m_aborted.store(true); - return true; - } - - void executeTask() override - { - if (thread() != m_thread_to_spawn_into) - connect(this, &Task::finished, this->thread(), &QThread::quit); - - m_dir.refresh(); - for (auto entry : m_dir.entryInfoList()) { - auto resource = m_create_func(entry); - resource->moveToThread(m_thread_to_spawn_into); - m_result->resources.insert(resource->internal_id(), resource); - } - - if (m_aborted) - emit finished(); - else - emitSucceeded(); - } - - private: - QDir m_dir; - ResultPtr m_result; - - std::atomic m_aborted = false; - - std::function m_create_func; - - /** This is the thread in which we should put new mod objects */ - QThread* m_thread_to_spawn_into; -}; diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.h b/launcher/minecraft/mod/tasks/LocalModParseTask.h index a03217093..ff1ff822e 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.h @@ -47,11 +47,6 @@ class LocalModParseTask : public Task { [[nodiscard]] int token() const { return m_token; } - private: - void processAsZip(); - void processAsFolder(); - void processAsLitemod(); - private: int m_token; ResourceType m_type; diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp similarity index 55% rename from launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp rename to launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp index 9f79ba098..1004bcff0 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp @@ -34,23 +34,20 @@ * limitations under the License. */ -#include "ModFolderLoadTask.h" +#include "ResourceFolderLoadTask.h" #include "minecraft/mod/MetadataHandler.h" #include -ModFolderLoadTask::ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan) - : Task(nullptr, false) - , m_mods_dir(mods_dir) - , m_index_dir(index_dir) - , m_is_indexed(is_indexed) - , m_clean_orphan(clean_orphan) - , m_result(new Result()) - , m_thread_to_spawn_into(thread()) +ResourceFolderLoadTask::ResourceFolderLoadTask(const QDir& resource_dir, + const QDir& index_dir, + bool is_indexed, + bool clean_orphan, + std::function create_function) {} -void ModFolderLoadTask::executeTask() +void ResourceFolderLoadTask::executeTask() { if (thread() != m_thread_to_spawn_into) connect(this, &Task::finished, this->thread(), &QThread::quit); @@ -63,32 +60,32 @@ void ModFolderLoadTask::executeTask() // Read JAR files that don't have metadata m_mods_dir.refresh(); for (auto entry : m_mods_dir.entryInfoList()) { - Mod* mod(new Mod(entry)); + Resource* resource = m_create_func(entry); - if (mod->enabled()) { - if (m_result->mods.contains(mod->internal_id())) { - m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); + if (resource->enabled()) { + if (m_result->resources.contains(resource->internal_id())) { + m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED); // Delete the object we just created, since a valid one is already in the mods list. - delete mod; + delete resource; } else { - m_result->mods[mod->internal_id()].reset(std::move(mod)); - m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); + m_result->resources[resource->internal_id()].reset(resource); + m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA); } } else { - QString chopped_id = mod->internal_id().chopped(9); - if (m_result->mods.contains(chopped_id)) { - m_result->mods[mod->internal_id()].reset(std::move(mod)); + QString chopped_id = resource->internal_id().chopped(9); + if (m_result->resources.contains(chopped_id)) { + m_result->resources[resource->internal_id()].reset(resource); - auto metadata = m_result->mods[chopped_id]->metadata(); + auto metadata = m_result->resources[chopped_id]->metadata(); if (metadata) { - mod->setMetadata(*metadata); + resource->setMetadata(*metadata); - m_result->mods[mod->internal_id()]->setStatus(ModStatus::Installed); - m_result->mods.remove(chopped_id); + m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::INSTALLED); + m_result->resources.remove(chopped_id); } } else { - m_result->mods[mod->internal_id()].reset(std::move(mod)); - m_result->mods[mod->internal_id()]->setStatus(ModStatus::NoMetadata); + m_result->resources[resource->internal_id()].reset(resource); + m_result->resources[resource->internal_id()]->setStatus(ResourceStatus::NO_METADATA); } } } @@ -96,17 +93,17 @@ void ModFolderLoadTask::executeTask() // Remove orphan metadata to prevent issues // See https://github.com/PolyMC/PolyMC/issues/996 if (m_clean_orphan) { - QMutableMapIterator iter(m_result->mods); + QMutableMapIterator iter(m_result->resources); while (iter.hasNext()) { - auto mod = iter.next().value(); - if (mod->status() == ModStatus::NotInstalled) { - mod->destroy(m_index_dir, false, false); + auto resource = iter.next().value(); + if (resource->status() == ResourceStatus::NOT_INSTALLED) { + resource->destroy(m_index_dir, false, false); iter.remove(); } } } - for (auto mod : m_result->mods) + for (auto mod : m_result->resources) mod->moveToThread(m_thread_to_spawn_into); if (m_aborted) @@ -115,18 +112,18 @@ void ModFolderLoadTask::executeTask() emitSucceeded(); } -void ModFolderLoadTask::getFromMetadata() +void ResourceFolderLoadTask::getFromMetadata() { m_index_dir.refresh(); for (auto entry : m_index_dir.entryList(QDir::Files)) { auto metadata = Metadata::get(m_index_dir, entry); - if (!metadata.isValid()) { + if (!metadata.isValid()) return; - } - auto* mod = new Mod(m_mods_dir, metadata); - mod->setStatus(ModStatus::NotInstalled); - m_result->mods[mod->internal_id()].reset(std::move(mod)); + auto* resource = m_create_func(QFileInfo(m_mods_dir.filePath(metadata.filename))); + resource->setMetadata(metadata); + resource->setStatus(ResourceStatus::NOT_INSTALLED); + m_result->resources[resource->internal_id()].reset(resource); } } diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h similarity index 84% rename from launcher/minecraft/mod/tasks/ModFolderLoadTask.h rename to launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h index 4200ef6d9..616597ca4 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h @@ -44,17 +44,21 @@ #include "minecraft/mod/Mod.h" #include "tasks/Task.h" -class ModFolderLoadTask : public Task { +class ResourceFolderLoadTask : public Task { Q_OBJECT public: struct Result { - QMap mods; + QMap resources; }; using ResultPtr = std::shared_ptr; ResultPtr result() const { return m_result; } public: - ModFolderLoadTask(QDir mods_dir, QDir index_dir, bool is_indexed, bool clean_orphan = false); + ResourceFolderLoadTask(const QDir& resource_dir, + const QDir& index_dir, + bool is_indexed, + bool clean_orphan, + std::function create_function); [[nodiscard]] bool canAbort() const override { return true; } bool abort() override @@ -76,6 +80,8 @@ class ModFolderLoadTask : public Task { std::atomic m_aborted = false; + std::function m_create_func; + /** This is the thread in which we should put new mod objects */ QThread* m_thread_to_spawn_into; }; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index c3eadd06d..05caa33c9 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -94,7 +94,7 @@ void EnsureMetadataTask::executeTask() } // They already have the right metadata :o - if (mod->status() != ModStatus::NoMetadata && mod->metadata() && mod->metadata()->provider == m_provider) { + if (mod->status() != ResourceStatus::NO_METADATA && mod->metadata() && mod->metadata()->provider == m_provider) { qDebug() << "Mod" << mod->name() << "already has metadata!"; emitReady(mod); continue; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.cpp b/launcher/modplatform/flame/FlameCheckUpdate.cpp index 476a4667a..6653e477e 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.cpp +++ b/launcher/modplatform/flame/FlameCheckUpdate.cpp @@ -154,7 +154,7 @@ void FlameCheckUpdate::executeTask() continue; } - if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ModStatus::NotInstalled)) { + if (!latest_ver.hash.isEmpty() && (mod->metadata()->hash != latest_ver.hash || mod->status() == ResourceStatus::NOT_INSTALLED)) { // Fake pack with the necessary info to pass to the download task :) auto pack = std::make_shared(); pack->name = mod->name(); @@ -167,7 +167,7 @@ void FlameCheckUpdate::executeTask() pack->provider = ModPlatform::ResourceProvider::FLAME; auto old_version = mod->version(); - if (old_version.isEmpty() && mod->status() != ModStatus::NotInstalled) { + 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; } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index c65f4fa80..574e99ac7 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -144,7 +144,7 @@ void ModrinthCheckUpdate::executeTask() auto mod = *mod_iter; auto key = project_ver.hash; - if ((key != hash && project_ver.is_preferred) || (mod->status() == ModStatus::NotInstalled)) { + if ((key != hash && project_ver.is_preferred) || (mod->status() == ResourceStatus::NOT_INSTALLED)) { if (mod->version() == project_ver.version_number) continue; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index a9ddb0c91..72b52836f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -121,7 +121,7 @@ void ModrinthPackExportTask::collectHashes() modIter != allMods.end()) { const Mod* mod = *modIter; if (mod->metadata() != nullptr) { - QUrl& url = mod->metadata()->url; + const QUrl& url = mod->metadata()->url; // ensure the url is permitted on modrinth.com if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) { qDebug() << "Resolving" << relative << "from index"; diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 7edc18cde..c49165557 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -35,6 +35,7 @@ auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool shoul class V1 { public: + // can also represent other resources beside loader mods - but this is what packwiz calls it struct Mod { QString slug{}; QString name{}; diff --git a/launcher/ui/dialogs/ModUpdateDialog.cpp b/launcher/ui/dialogs/ModUpdateDialog.cpp index 1f0fa7cd2..f1216572f 100644 --- a/launcher/ui/dialogs/ModUpdateDialog.cpp +++ b/launcher/ui/dialogs/ModUpdateDialog.cpp @@ -228,7 +228,7 @@ auto ModUpdateDialog::ensureMetadata() -> bool }; for (auto candidate : m_candidates) { - if (candidate->status() != ModStatus::NoMetadata) { + if (candidate->status() != ResourceStatus::NO_METADATA) { onMetadataEnsured(candidate); continue; } From ee487669960ba8ee7c1997621b9fd7e778d3f65a Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Sep 2023 00:27:05 +0100 Subject: [PATCH 02/39] More generalistaion for ResourceFolderModels Signed-off-by: TheKodeToad --- launcher/ResourceDownloadTask.cpp | 8 +- launcher/minecraft/mod/MetadataHandler.h | 14 +-- launcher/minecraft/mod/Mod.cpp | 1 - launcher/minecraft/mod/ModFolderModel.cpp | 95 ------------------- launcher/minecraft/mod/ModFolderModel.h | 10 -- launcher/minecraft/mod/Resource.cpp | 9 +- launcher/minecraft/mod/Resource.h | 3 +- .../minecraft/mod/ResourceFolderModel.cpp | 58 ++++++++++- launcher/minecraft/mod/ResourceFolderModel.h | 8 +- launcher/modplatform/packwiz/Packwiz.cpp | 16 ++-- launcher/modplatform/packwiz/Packwiz.h | 16 ++-- launcher/ui/MainWindow.cpp | 2 +- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- 13 files changed, 90 insertions(+), 152 deletions(-) diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index a02151ca1..dffbaf478 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -67,12 +67,8 @@ void ResourceDownloadTask::downloadSucceeded() m_filesNetJob.reset(); auto name = std::get<0>(to_delete); auto filename = std::get<1>(to_delete); - if (!name.isEmpty() && filename != m_pack_version.fileName) { - if (auto model = dynamic_cast(m_pack_model.get()); model) - model->uninstallMod(filename, true); - else - m_pack_model->uninstallResource(filename); - } + if (!name.isEmpty() && filename != m_pack_version.fileName) + m_pack_model->uninstallResource(filename, true); } void ResourceDownloadTask::downloadFailed(QString reason) diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 747375d3f..3794db484 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -28,37 +28,37 @@ class Mod; namespace Metadata { using ModStruct = Packwiz::V1::Mod; -inline auto create(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct +inline auto create(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> ModStruct { return Packwiz::V1::createModFormat(index_dir, mod_pack, mod_version); } -inline auto create(QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct +inline auto create(const QDir& index_dir, Mod& internal_mod, QString mod_slug) -> ModStruct { return Packwiz::V1::createModFormat(index_dir, internal_mod, std::move(mod_slug)); } -inline void update(QDir& index_dir, ModStruct& mod) +inline void update(const QDir& index_dir, ModStruct& mod) { Packwiz::V1::updateModIndex(index_dir, mod); } -inline void remove(QDir& index_dir, QString mod_slug) +inline void remove(const QDir& index_dir, QString mod_slug) { Packwiz::V1::deleteModIndex(index_dir, mod_slug); } -inline void remove(QDir& index_dir, QVariant& mod_id) +inline void remove(const QDir& index_dir, QVariant& mod_id) { Packwiz::V1::deleteModIndex(index_dir, mod_id); } -inline auto get(QDir& index_dir, QString mod_slug) -> ModStruct +inline auto get(const QDir& index_dir, QString mod_slug) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, std::move(mod_slug)); } -inline auto get(QDir& index_dir, QVariant& mod_id) -> ModStruct +inline auto get(const QDir& index_dir, QVariant& mod_id) -> ModStruct { return Packwiz::V1::getIndexForMod(index_dir, mod_id); } diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 53f7d72de..27688b5f2 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -36,7 +36,6 @@ #include "Mod.h" -#include #include #include #include diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 2f0e9b3e4..733e1f655 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -51,13 +51,9 @@ #include "Application.h" -#include "Json.h" #include "Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" -#include "minecraft/mod/tasks/ResourceFolderLoadTask.h" -#include "modplatform/ModIndex.h" -#include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) @@ -184,58 +180,11 @@ Task* ModFolderModel::createParseTask(Resource& resource) return new LocalModParseTask(m_next_resolution_ticket, resource.type(), resource.fileinfo()); } -bool ModFolderModel::uninstallMod(const QString& filename, bool preserve_metadata) -{ - for (auto mod : allMods()) { - if (mod->fileinfo().fileName() == filename) { - auto index_dir = indexDir(); - mod->destroy(index_dir, preserve_metadata, false); - - update(); - - return true; - } - } - - return false; -} - -bool ModFolderModel::deleteMods(const QModelIndexList& indexes) -{ - if (indexes.isEmpty()) - return true; - - for (auto i : indexes) { - if (i.column() != 0) { - continue; - } - auto m = at(i.row()); - auto index_dir = indexDir(); - m->destroy(index_dir); - } - - update(); - - return true; -} - bool ModFolderModel::isValid() { return m_dir.exists() && m_dir.isReadable(); } -bool ModFolderModel::startWatching() -{ - // Remove orphaned metadata next time - m_first_folder_load = true; - return ResourceFolderModel::startWatching({ m_dir.absolutePath(), indexDir().absolutePath() }); -} - -bool ModFolderModel::stopWatching() -{ - return ResourceFolderModel::stopWatching({ m_dir.absolutePath(), indexDir().absolutePath() }); -} - auto ModFolderModel::selectedMods(QModelIndexList& indexes) -> QList { QList selected_resources; @@ -280,47 +229,3 @@ void ModFolderModel::onParseSucceeded(int ticket, QString mod_id) emit dataChanged(index(row), index(row, columnCount(QModelIndex()) - 1)); } - -static const FlameAPI flameAPI; -bool ModFolderModel::installMod(QString file_path, ModPlatform::IndexedVersion& vers) -{ - if (vers.addonId.isValid()) { - ModPlatform::IndexedPack pack{ - vers.addonId, - ModPlatform::ResourceProvider::FLAME, - }; - - QEventLoop loop; - - auto response = std::make_shared(); - auto job = flameAPI.getProject(vers.addonId.toString(), response); - - QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); }); - QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit); - QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] { - QJsonParseError parse_error{}; - QJsonDocument doc = QJsonDocument::fromJson(*response, &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() << *response; - return; - } - try { - auto obj = Json::requireObject(Json::requireObject(doc), "data"); - FlameMod::loadIndexedPack(pack, obj); - } catch (const JSONValidationError& e) { - qDebug() << doc; - qWarning() << "Error while reading mod info: " << e.cause(); - } - LocalModUpdateTask update_metadata(indexDir(), pack, vers); - QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); - update_metadata.start(); - }); - - job->start(); - - loop.exec(); - } - return ResourceFolderModel::installResource(file_path); -} diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 9326158d4..c1db33c0c 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -74,18 +74,8 @@ class ModFolderModel : public ResourceFolderModel { [[nodiscard]] Resource* createResource(const QFileInfo& file) override { return new Mod(file); } [[nodiscard]] Task* createParseTask(Resource&) override; - bool installMod(QString file_path) { return ResourceFolderModel::installResource(file_path); } - bool installMod(QString file_path, ModPlatform::IndexedVersion& vers); - bool uninstallMod(const QString& filename, bool preserve_metadata = false); - - /// Deletes all the selected mods - bool deleteMods(const QModelIndexList& indexes); - bool isValid(); - bool startWatching() override; - bool stopWatching() override; - auto selectedMods(QModelIndexList& indexes) -> QList; auto allMods() -> QList; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 0dd7f8992..bfe2a8802 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -172,15 +172,10 @@ bool Resource::enable(EnableAction action) return true; } -bool Resource::destroy(bool attemptTrash) +auto Resource::destroy(const QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool { m_type = ResourceType::UNKNOWN; - return (attemptTrash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); -} - -auto Resource::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_trash) -> bool -{ if (!preserve_metadata) { qDebug() << QString("Destroying metadata for '%1' on purpose").arg(name()); @@ -192,7 +187,7 @@ auto Resource::destroy(QDir& index_dir, bool preserve_metadata, bool attempt_tra } } - return destroy(attempt_trash); + return (attempt_trash && FS::trash(m_file_info.filePath())) || FS::deletePath(m_file_info.filePath()); } bool Resource::isSymLinkUnder(const QString& instPath) const diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 5012c23a4..77063cbf0 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -99,8 +99,7 @@ class Resource : public QObject { } // Delete all files of this resource. - auto destroy(bool attemptTrash = true) -> bool; - auto destroy(QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; + auto destroy(const QDir& index_dir, bool preserve_metadata = false, bool attempt_trash = true) -> bool; [[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 a39e9adee..9ac387904 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include "Application.h" #include "FileSystem.h" @@ -17,6 +18,10 @@ #include "QVariantUtils.h" #include "minecraft/mod/tasks/ResourceFolderLoadTask.h" +#include "Json.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "modplatform/flame/FlameAPI.h" +#include "modplatform/flame/FlameModIndex.h" #include "settings/Setting.h" #include "tasks/Task.h" #include "ui/dialogs/CustomMessageBox.h" @@ -43,6 +48,9 @@ ResourceFolderModel::~ResourceFolderModel() bool ResourceFolderModel::startWatching(const QStringList paths) { + // Remove orphaned metadata next time + m_first_folder_load = true; + if (m_is_watching) return false; @@ -153,11 +161,55 @@ bool ResourceFolderModel::installResource(QString original_path) return false; } -bool ResourceFolderModel::uninstallResource(QString file_name) +bool ResourceFolderModel::installResource(QString path, ModPlatform::IndexedVersion& vers) +{ + if (vers.addonId.isValid()) { + ModPlatform::IndexedPack pack{ + vers.addonId, + ModPlatform::ResourceProvider::FLAME, + }; + + QEventLoop loop; + + auto response = std::make_shared(); + auto job = FlameAPI().getProject(vers.addonId.toString(), response); + + QObject::connect(job.get(), &Task::failed, [&loop] { loop.quit(); }); + QObject::connect(job.get(), &Task::aborted, &loop, &QEventLoop::quit); + QObject::connect(job.get(), &Task::succeeded, [response, this, &vers, &loop, &pack] { + QJsonParseError parse_error{}; + QJsonDocument doc = QJsonDocument::fromJson(*response, &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() << *response; + return; + } + try { + auto obj = Json::requireObject(Json::requireObject(doc), "data"); + FlameMod::loadIndexedPack(pack, obj); + } catch (const JSONValidationError& e) { + qDebug() << doc; + qWarning() << "Error while reading mod info: " << e.cause(); + } + LocalModUpdateTask update_metadata(indexDir(), pack, vers); + QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); + update_metadata.start(); + }); + + job->start(); + + loop.exec(); + } + + return installResource(std::move(path)); +} + +bool ResourceFolderModel::uninstallResource(QString file_name, bool preserve_metadata) { for (auto& resource : m_resources) { if (resource->fileinfo().fileName() == file_name) { - auto res = resource->destroy(false); + auto res = resource->destroy(indexDir(), preserve_metadata, false); update(); @@ -179,7 +231,7 @@ bool ResourceFolderModel::deleteResources(const QModelIndexList& indexes) auto& resource = m_resources.at(i.row()); - resource->destroy(); + resource->destroy(indexDir()); } update(); diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3a4287b30..e8f8d2b9e 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -49,8 +49,8 @@ class ResourceFolderModel : public QAbstractListModel { bool stopWatching(const QStringList paths); /* Helper methods for subclasses, using a predetermined list of paths. */ - virtual bool startWatching() { return startWatching({ m_dir.absolutePath() }); } - virtual bool stopWatching() { return stopWatching({ m_dir.absolutePath() }); } + virtual bool startWatching() { return startWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } + virtual bool stopWatching() { return stopWatching({ indexDir().absolutePath(), m_dir.absolutePath() }); } QDir indexDir() { return { QString("%1/.index").arg(dir().absolutePath()) }; } @@ -61,11 +61,13 @@ class ResourceFolderModel : public QAbstractListModel { */ virtual bool installResource(QString path); + virtual bool installResource(QString path, ModPlatform::IndexedVersion& vers); + /** Uninstall (i.e. remove all data about it) a resource, given its file name. * * Returns whether the removal was successful. */ - virtual bool uninstallResource(QString file_name); + virtual bool uninstallResource(QString file_name, bool preserve_metadata = false); virtual bool deleteResources(const QModelIndexList&); /** Applies the given 'action' to the resources in 'indexes'. diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 71f66bf3e..bfc1dea44 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -32,7 +32,7 @@ namespace Packwiz { -auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString +auto getRealIndexName(const QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString { QFile index_file(index_dir.absoluteFilePath(normalized_fname)); @@ -89,7 +89,7 @@ auto intEntry(toml::table table, QString entry_name) -> int return node.value_or(0); } -auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) +auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -115,7 +115,7 @@ auto V1::createModFormat([[maybe_unused]] QDir& index_dir, ModPlatform::IndexedP return mod; } -auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod +auto V1::createModFormat(const QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, QString slug) -> Mod { // Try getting metadata if it exists Mod mod{ getIndexForMod(index_dir, slug) }; @@ -127,7 +127,7 @@ auto V1::createModFormat(QDir& index_dir, [[maybe_unused]] ::Mod& internal_mod, return {}; } -void V1::updateModIndex(QDir& index_dir, Mod& mod) +void V1::updateModIndex(const QDir& index_dir, Mod& mod) { if (!mod.isValid()) { qCritical() << QString("Tried to update metadata of an invalid mod!"); @@ -192,7 +192,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) index_file.close(); } -void V1::deleteModIndex(QDir& index_dir, QString& mod_slug) +void V1::deleteModIndex(const QDir& index_dir, QString& mod_slug) { auto normalized_fname = indexFileName(mod_slug); auto real_fname = getRealIndexName(index_dir, normalized_fname); @@ -211,7 +211,7 @@ void V1::deleteModIndex(QDir& index_dir, QString& mod_slug) } } -void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id) +void V1::deleteModIndex(const QDir& index_dir, QVariant& mod_id) { for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { auto mod = getIndexForMod(index_dir, file_name); @@ -223,7 +223,7 @@ void V1::deleteModIndex(QDir& index_dir, QVariant& mod_id) } } -auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod +auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { Mod mod; @@ -301,7 +301,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString slug) -> Mod return mod; } -auto V1::getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod +auto V1::getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod { for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { auto mod = getIndexForMod(index_dir, file_name); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index c49165557..ffc1d40c8 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -31,7 +31,7 @@ class Mod; namespace Packwiz { -auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; +auto getRealIndexName(const QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; class V1 { public: @@ -67,33 +67,33 @@ class V1 { /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher, when downloading mods. * */ - static auto createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod; + static auto createModFormat(const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod; /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher, plus a necessary slug. * */ - static auto createModFormat(QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod; + static auto createModFormat(const QDir& index_dir, ::Mod& internal_mod, QString slug) -> Mod; /* Updates the mod index for the provided mod. * This creates a new index if one does not exist already * TODO: Ask the user if they want to override, and delete the old mod's files, or keep the old one. * */ - static void updateModIndex(QDir& index_dir, Mod& mod); + static void updateModIndex(const QDir& index_dir, Mod& mod); /* Deletes the metadata for the mod with the given slug. If the metadata doesn't exist, it does nothing. */ - static void deleteModIndex(QDir& index_dir, QString& mod_slug); + static void deleteModIndex(const QDir& index_dir, QString& mod_slug); /* Deletes the metadata for the mod with the given id. If the metadata doesn't exist, it does nothing. */ - static void deleteModIndex(QDir& index_dir, QVariant& mod_id); + static void deleteModIndex(const QDir& index_dir, QVariant& mod_id); /* Gets the metadata for a mod with a particular file name. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QString slug) -> Mod; + static auto getIndexForMod(const QDir& index_dir, QString slug) -> Mod; /* Gets the metadata for a mod with a particular id. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QVariant& mod_id) -> Mod; + static auto getIndexForMod(const QDir& index_dir, QVariant& mod_id) -> Mod; }; } // namespace Packwiz diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 5e55a5abb..a7e3def38 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1048,7 +1048,7 @@ void MainWindow::processURLs(QList urls) qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName; break; case PackedResourceType::Mod: - minecraftInst->loaderModList()->installMod(localFileName, version); + minecraftInst->loaderModList()->installResource(localFileName, version); break; case PackedResourceType::ShaderPack: minecraftInst->shaderPackList()->installResource(localFileName); diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 0f5e29cb6..6333dec1d 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -150,7 +150,7 @@ void ModFolderPage::removeItems(const QItemSelection& selection) if (response != QMessageBox::Yes) return; } - m_model->deleteMods(selection.indexes()); + m_model->deleteResources(selection.indexes()); } void ModFolderPage::installMods() From 6aecbfc38fe3ec2b6fc6d9f3e2a76fc03229729b Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Sep 2023 12:50:20 +0100 Subject: [PATCH 03/39] Fix crashes Signed-off-by: TheKodeToad --- launcher/QObjectPtr.h | 6 +++++- .../minecraft/mod/tasks/ResourceFolderLoadTask.cpp | 14 +++++++++++--- .../minecraft/mod/tasks/ResourceFolderLoadTask.h | 5 ++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/launcher/QObjectPtr.h b/launcher/QObjectPtr.h index 5f7b64df0..88c17c0b2 100644 --- a/launcher/QObjectPtr.h +++ b/launcher/QObjectPtr.h @@ -33,7 +33,11 @@ class shared_qobject_ptr : public QSharedPointer { {} void reset() { QSharedPointer::reset(); } - void reset(T* other) { QSharedPointer::reset(other); } + void reset(T* other) + { + shared_qobject_ptr t(other); + this->swap(t); + } void reset(const shared_qobject_ptr& other) { shared_qobject_ptr t(other); diff --git a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp index 1004bcff0..2a02eb177 100644 --- a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.cpp @@ -45,6 +45,14 @@ ResourceFolderLoadTask::ResourceFolderLoadTask(const QDir& resource_dir, bool is_indexed, bool clean_orphan, std::function create_function) + : Task(nullptr, false) + , m_resource_dir(resource_dir) + , m_index_dir(index_dir) + , m_is_indexed(is_indexed) + , m_clean_orphan(clean_orphan) + , m_create_func(create_function) + , m_result(new Result()) + , m_thread_to_spawn_into(thread()) {} void ResourceFolderLoadTask::executeTask() @@ -58,8 +66,8 @@ void ResourceFolderLoadTask::executeTask() } // Read JAR files that don't have metadata - m_mods_dir.refresh(); - for (auto entry : m_mods_dir.entryInfoList()) { + m_resource_dir.refresh(); + for (auto entry : m_resource_dir.entryInfoList()) { Resource* resource = m_create_func(entry); if (resource->enabled()) { @@ -121,7 +129,7 @@ void ResourceFolderLoadTask::getFromMetadata() if (!metadata.isValid()) return; - auto* resource = m_create_func(QFileInfo(m_mods_dir.filePath(metadata.filename))); + auto* resource = m_create_func(QFileInfo(m_resource_dir.filePath(metadata.filename))); resource->setMetadata(metadata); resource->setStatus(ResourceStatus::NOT_INSTALLED); m_result->resources[resource->internal_id()].reset(resource); diff --git a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h index 616597ca4..9950345ef 100644 --- a/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ResourceFolderLoadTask.h @@ -73,15 +73,14 @@ class ResourceFolderLoadTask : public Task { void getFromMetadata(); private: - QDir m_mods_dir, m_index_dir; + QDir m_resource_dir, m_index_dir; bool m_is_indexed; bool m_clean_orphan; + std::function m_create_func; ResultPtr m_result; std::atomic m_aborted = false; - std::function m_create_func; - /** This is the thread in which we should put new mod objects */ QThread* m_thread_to_spawn_into; }; From ad16d612087beab6f918c96168ad5628e77487f5 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Sep 2023 17:42:05 +0100 Subject: [PATCH 04/39] Resource metadata writing Signed-off-by: TheKodeToad --- launcher/CMakeLists.txt | 4 +- launcher/ResourceDownloadTask.cpp | 6 +- launcher/ResourceDownloadTask.h | 4 +- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 4 +- ...teTask.cpp => LocalResourceUpdateTask.cpp} | 26 +-- ...UpdateTask.h => LocalResourceUpdateTask.h} | 12 +- launcher/modplatform/EnsureMetadataTask.cpp | 168 +++++++++--------- launcher/modplatform/EnsureMetadataTask.h | 23 +-- .../ui/pages/modplatform/ResourcePage.cpp | 4 +- .../ui/pages/modplatform/ShaderPackPage.cpp | 4 +- 11 files changed, 131 insertions(+), 126 deletions(-) rename launcher/minecraft/mod/tasks/{LocalModUpdateTask.cpp => LocalResourceUpdateTask.cpp} (63%) rename launcher/minecraft/mod/tasks/{LocalModUpdateTask.h => LocalResourceUpdateTask.h} (74%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index cd4c64dec..5bc2d73a7 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -354,8 +354,8 @@ set(MINECRAFT_SOURCES minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.cpp - minecraft/mod/tasks/LocalModUpdateTask.h - minecraft/mod/tasks/LocalModUpdateTask.cpp + minecraft/mod/tasks/LocalResourceUpdateTask.h + minecraft/mod/tasks/LocalResourceUpdateTask.cpp minecraft/mod/tasks/LocalDataPackParseTask.h minecraft/mod/tasks/LocalDataPackParseTask.cpp minecraft/mod/tasks/LocalResourcePackParseTask.h diff --git a/launcher/ResourceDownloadTask.cpp b/launcher/ResourceDownloadTask.cpp index dffbaf478..e5828b569 100644 --- a/launcher/ResourceDownloadTask.cpp +++ b/launcher/ResourceDownloadTask.cpp @@ -33,9 +33,9 @@ ResourceDownloadTask::ResourceDownloadTask(ModPlatform::IndexedPack::Ptr pack, QString custom_target_folder) : m_pack(std::move(pack)), m_pack_version(std::move(version)), m_pack_model(packs), m_custom_target_folder(custom_target_folder) { - if (auto model = dynamic_cast(m_pack_model.get()); model && is_indexed) { - m_update_task.reset(new LocalModUpdateTask(model->indexDir(), *m_pack, m_pack_version)); - connect(m_update_task.get(), &LocalModUpdateTask::hasOldMod, this, &ResourceDownloadTask::hasOldResource); + if (is_indexed) { + m_update_task.reset(new LocalResourceUpdateTask(m_pack_model->indexDir(), *m_pack, m_pack_version)); + connect(m_update_task.get(), &LocalResourceUpdateTask::hasOldResource, this, &ResourceDownloadTask::hasOldResource); addTask(m_update_task); } diff --git a/launcher/ResourceDownloadTask.h b/launcher/ResourceDownloadTask.h index 2baddf8a8..7da5a4aad 100644 --- a/launcher/ResourceDownloadTask.h +++ b/launcher/ResourceDownloadTask.h @@ -22,7 +22,7 @@ #include "net/NetJob.h" #include "tasks/SequentialTask.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/ModIndex.h" class ResourceFolderModel; @@ -50,7 +50,7 @@ class ResourceDownloadTask : public SequentialTask { QString m_custom_target_folder; NetJob::Ptr m_filesNetJob; - LocalModUpdateTask::Ptr m_update_task; + LocalResourceUpdateTask::Ptr m_update_task; void downloadProgressChanged(qint64 current, qint64 total); void downloadFailed(QString reason); diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 733e1f655..43c64a667 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -53,7 +53,7 @@ #include "Resource.h" #include "minecraft/mod/tasks/LocalModParseTask.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FlameModIndex.h" ModFolderModel::ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 9ac387904..27eb4d2e5 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -19,7 +19,7 @@ #include "minecraft/mod/tasks/ResourceFolderLoadTask.h" #include "Json.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" #include "settings/Setting.h" @@ -192,7 +192,7 @@ bool ResourceFolderModel::installResource(QString path, ModPlatform::IndexedVers qDebug() << doc; qWarning() << "Error while reading mod info: " << e.cause(); } - LocalModUpdateTask update_metadata(indexDir(), pack, vers); + LocalResourceUpdateTask update_metadata(indexDir(), pack, vers); QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); update_metadata.start(); }); diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.cpp similarity index 63% rename from launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp rename to launcher/minecraft/mod/tasks/LocalResourceUpdateTask.cpp index 4352fad91..c8fe1050a 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.cpp @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -#include "LocalModUpdateTask.h" +#include "LocalResourceUpdateTask.h" #include "FileSystem.h" #include "minecraft/mod/MetadataHandler.h" @@ -26,12 +26,12 @@ #include #endif -LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version) - : m_index_dir(index_dir), m_mod(mod), m_mod_version(mod_version) +LocalResourceUpdateTask::LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version) + : m_index_dir(index_dir), m_project(project), m_version(version) { // Ensure a '.index' folder exists in the mods folder, and create it if it does not if (!FS::ensureFolderPathExists(index_dir.path())) { - emitFailed(QString("Unable to create index for mod %1!").arg(m_mod.name)); + emitFailed(QString("Unable to create index directory at %1!").arg(index_dir.absolutePath())); } #ifdef Q_OS_WIN32 @@ -39,28 +39,28 @@ LocalModUpdateTask::LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& #endif } -void LocalModUpdateTask::executeTask() +void LocalResourceUpdateTask::executeTask() { - setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name)); + setStatus(tr("Updating index for resource:\n%1").arg(m_project.name)); - auto old_metadata = Metadata::get(m_index_dir, m_mod.addonId); + auto old_metadata = Metadata::get(m_index_dir, m_project.addonId); if (old_metadata.isValid()) { - emit hasOldMod(old_metadata.name, old_metadata.filename); - if (m_mod.slug.isEmpty()) - m_mod.slug = old_metadata.slug; + emit hasOldResource(old_metadata.name, old_metadata.filename); + if (m_project.slug.isEmpty()) + m_project.slug = old_metadata.slug; } - auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version); + auto pw_mod = Metadata::create(m_index_dir, m_project, m_version); if (pw_mod.isValid()) { Metadata::update(m_index_dir, pw_mod); emitSucceeded(); } else { - qCritical() << "Tried to update an invalid mod!"; + qCritical() << "Tried to update an invalid resource!"; emitFailed(tr("Invalid metadata")); } } -auto LocalModUpdateTask::abort() -> bool +auto LocalResourceUpdateTask::abort() -> bool { emitAborted(); return true; diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.h similarity index 74% rename from launcher/minecraft/mod/tasks/LocalModUpdateTask.h rename to launcher/minecraft/mod/tasks/LocalResourceUpdateTask.h index 080999294..6e2efbd6a 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalResourceUpdateTask.h @@ -23,12 +23,12 @@ #include "modplatform/ModIndex.h" #include "tasks/Task.h" -class LocalModUpdateTask : public Task { +class LocalResourceUpdateTask : public Task { Q_OBJECT public: - using Ptr = shared_qobject_ptr; + using Ptr = shared_qobject_ptr; - explicit LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version); + explicit LocalResourceUpdateTask(QDir index_dir, ModPlatform::IndexedPack& project, ModPlatform::IndexedVersion& version); auto canAbort() const -> bool override { return true; } auto abort() -> bool override; @@ -38,10 +38,10 @@ class LocalModUpdateTask : public Task { void executeTask() override; signals: - void hasOldMod(QString name, QString filename); + void hasOldResource(QString name, QString filename); private: QDir m_index_dir; - ModPlatform::IndexedPack& m_mod; - ModPlatform::IndexedVersion& m_mod_version; + ModPlatform::IndexedPack& m_project; + ModPlatform::IndexedVersion& m_version; }; diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index 05caa33c9..4adbebdb1 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -6,7 +6,7 @@ #include "Json.h" #include "minecraft/mod/Mod.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "minecraft/mod/tasks/LocalResourceUpdateTask.h" #include "modplatform/flame/FlameAPI.h" #include "modplatform/flame/FlameModIndex.h" @@ -19,52 +19,52 @@ static ModPlatform::ProviderCapabilities ProviderCaps; static ModrinthAPI modrinth_api; static FlameAPI flame_api; -EnsureMetadataTask::EnsureMetadataTask(Mod* mod, QDir dir, ModPlatform::ResourceProvider prov) +EnsureMetadataTask::EnsureMetadataTask(Resource* resource, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_hashing_task(nullptr), m_current_task(nullptr) { - auto hash_task = createNewHash(mod); + auto hash_task = createNewHash(resource); if (!hash_task) return; - connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); }); - connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); }); + connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); }); + connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); }); hash_task->start(); } -EnsureMetadataTask::EnsureMetadataTask(QList& mods, QDir dir, ModPlatform::ResourceProvider prov) +EnsureMetadataTask::EnsureMetadataTask(QList& resources, QDir dir, ModPlatform::ResourceProvider prov) : Task(nullptr), m_index_dir(dir), m_provider(prov), m_current_task(nullptr) { m_hashing_task.reset(new ConcurrentTask(this, "MakeHashesTask", 10)); - for (auto* mod : mods) { - auto hash_task = createNewHash(mod); + for (auto* resource : resources) { + auto hash_task = createNewHash(resource); if (!hash_task) continue; - connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, mod](QString hash) { m_mods.insert(hash, mod); }); - connect(hash_task.get(), &Task::failed, [this, mod] { emitFail(mod, "", RemoveFromList::No); }); + connect(hash_task.get(), &Hashing::Hasher::resultsReady, [this, resource](QString hash) { m_resources.insert(hash, resource); }); + connect(hash_task.get(), &Task::failed, [this, resource] { emitFail(resource, "", RemoveFromList::No); }); m_hashing_task->addTask(hash_task); } } -Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Mod* mod) +Hashing::Hasher::Ptr EnsureMetadataTask::createNewHash(Resource* resource) { - if (!mod || !mod->valid() || mod->type() == ResourceType::FOLDER) + if (!resource || !resource->valid() || resource->type() == ResourceType::FOLDER) return nullptr; - return Hashing::createHasher(mod->fileinfo().absoluteFilePath(), m_provider); + return Hashing::createHasher(resource->fileinfo().absoluteFilePath(), m_provider); } -QString EnsureMetadataTask::getExistingHash(Mod* mod) +QString EnsureMetadataTask::getExistingHash(Resource* resource) { // Check for already computed hashes // (linear on the number of mods vs. linear on the size of the mod's JAR) - auto it = m_mods.keyValueBegin(); - while (it != m_mods.keyValueEnd()) { - if ((*it).second == mod) + auto it = m_resources.keyValueBegin(); + while (it != m_resources.keyValueEnd()) { + if ((*it).second == resource) break; it++; } // We already have the hash computed - if (it != m_mods.keyValueEnd()) { + if (it != m_resources.keyValueEnd()) { return (*it).first; } @@ -84,25 +84,25 @@ bool EnsureMetadataTask::abort() void EnsureMetadataTask::executeTask() { - setStatus(tr("Checking if mods have metadata...")); + setStatus(tr("Checking if resources have metadata...")); - for (auto* mod : m_mods) { - if (!mod->valid()) { - qDebug() << "Mod" << mod->name() << "is invalid!"; - emitFail(mod); + for (auto* resource : m_resources) { + if (!resource->valid()) { + qDebug() << "Resource" << resource->name() << "is invalid!"; + emitFail(resource); continue; } // They already have the right metadata :o - if (mod->status() != ResourceStatus::NO_METADATA && mod->metadata() && mod->metadata()->provider == m_provider) { - qDebug() << "Mod" << mod->name() << "already has metadata!"; - emitReady(mod); + if (resource->status() != ResourceStatus::NO_METADATA && resource->metadata() && resource->metadata()->provider == m_provider) { + qDebug() << "Resource" << resource->name() << "already has metadata!"; + emitReady(resource); continue; } // Folders don't have metadata - if (mod->type() == ResourceType::FOLDER) { - emitReady(mod); + if (resource->type() == ResourceType::FOLDER) { + emitReady(resource); } } @@ -118,9 +118,9 @@ void EnsureMetadataTask::executeTask() } auto invalidade_leftover = [this] { - for (auto mod = m_mods.constBegin(); mod != m_mods.constEnd(); mod++) - emitFail(mod.value(), mod.key(), RemoveFromList::No); - m_mods.clear(); + for (auto resource = m_resources.constBegin(); resource != m_resources.constEnd(); resource++) + emitFail(resource.value(), resource.key(), RemoveFromList::No); + m_resources.clear(); emitSucceeded(); }; @@ -159,53 +159,53 @@ void EnsureMetadataTask::executeTask() m_current_task.reset(); }); - if (m_mods.size() > 1) + if (m_resources.size() > 1) setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); - else if (!m_mods.empty()) + else if (!m_resources.empty()) setStatus(tr("Requesting metadata information from %1 for '%2'...") - .arg(ProviderCaps.readableName(m_provider), m_mods.begin().value()->name())); + .arg(ProviderCaps.readableName(m_provider), m_resources.begin().value()->name())); m_current_task = version_task; version_task->start(); } -void EnsureMetadataTask::emitReady(Mod* m, QString key, RemoveFromList remove) +void EnsureMetadataTask::emitReady(Resource* resource, QString key, RemoveFromList remove) { - if (!m) { - qCritical() << "Tried to mark a null mod as ready."; + if (!resource) { + qCritical() << "Tried to mark a null resource as ready."; if (!key.isEmpty()) - m_mods.remove(key); + m_resources.remove(key); return; } - qDebug() << QString("Generated metadata for %1").arg(m->name()); - emit metadataReady(m); + qDebug() << QString("Generated metadata for %1").arg(resource->name()); + emit metadataReady(resource); if (remove == RemoveFromList::Yes) { if (key.isEmpty()) - key = getExistingHash(m); - m_mods.remove(key); + key = getExistingHash(resource); + m_resources.remove(key); } } -void EnsureMetadataTask::emitFail(Mod* m, QString key, RemoveFromList remove) +void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromList remove) { - if (!m) { - qCritical() << "Tried to mark a null mod as failed."; + if (!resource) { + qCritical() << "Tried to mark a null resource as failed."; if (!key.isEmpty()) - m_mods.remove(key); + m_resources.remove(key); return; } - qDebug() << QString("Failed to generate metadata for %1").arg(m->name()); - emit metadataFailed(m); + qDebug() << QString("Failed to generate metadata for %1").arg(resource->name()); + emit metadataFailed(resource); if (remove == RemoveFromList::Yes) { if (key.isEmpty()) - key = getExistingHash(m); - m_mods.remove(key); + key = getExistingHash(resource); + m_resources.remove(key); } } @@ -216,7 +216,7 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask() auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); auto response = std::make_shared(); - auto ver_task = modrinth_api.currentVersions(m_mods.keys(), hash_type, response); + auto ver_task = modrinth_api.currentVersions(m_resources.keys(), hash_type, response); // Prevents unfortunate timings when aborting the task if (!ver_task) @@ -236,20 +236,20 @@ Task::Ptr EnsureMetadataTask::modrinthVersionsTask() try { auto entries = Json::requireObject(doc); - for (auto& hash : m_mods.keys()) { - auto mod = m_mods.find(hash).value(); + for (auto& hash : m_resources.keys()) { + auto resource = m_resources.find(hash).value(); try { auto entry = Json::requireObject(entries, hash); - setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name())); - qDebug() << "Getting version for" << mod->name() << "from Modrinth"; + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name())); + qDebug() << "Getting version for" << resource->name() << "from Modrinth"; m_temp_versions.insert(hash, Modrinth::loadIndexedPackVersion(entry)); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; - emitFail(mod); + emitFail(resource); } } } catch (Json::JsonException& e) { @@ -321,23 +321,23 @@ Task::Ptr EnsureMetadataTask::modrinthProjectsTask() auto hash = addonIds.find(pack.addonId.toString()).value(); - auto mod_iter = m_mods.find(hash); - if (mod_iter == m_mods.end()) { + auto resource_iter = m_resources.find(hash); + if (resource_iter == m_resources.end()) { qWarning() << "Invalid project id from the API response."; continue; } - auto* mod = mod_iter.value(); + auto* resource = resource_iter.value(); try { - setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(mod->name())); + setStatus(tr("Parsing API response from Modrinth for '%1'...").arg(resource->name())); - modrinthCallback(pack, m_temp_versions.find(hash).value(), mod); + modrinthCallback(pack, m_temp_versions.find(hash).value(), resource); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; - emitFail(mod); + emitFail(resource); } } }); @@ -351,7 +351,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask() auto response = std::make_shared(); QList fingerprints; - for (auto& murmur : m_mods.keys()) { + for (auto& murmur : m_resources.keys()) { fingerprints.push_back(murmur.toUInt()); } @@ -391,13 +391,13 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask() } auto fingerprint = QString::number(Json::ensureVariant(file_obj, "fileFingerprint").toUInt()); - auto mod = m_mods.find(fingerprint); - if (mod == m_mods.end()) { + auto resource = m_resources.find(fingerprint); + if (resource == m_resources.end()) { qWarning() << "Invalid fingerprint from the API response."; continue; } - setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*mod)->name())); + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg((*resource)->name())); m_temp_versions.insert(fingerprint, FlameMod::loadIndexedPackVersion(file_obj)); } @@ -414,7 +414,7 @@ Task::Ptr EnsureMetadataTask::flameVersionsTask() Task::Ptr EnsureMetadataTask::flameProjectsTask() { QHash addonIds; - for (auto const& hash : m_mods.keys()) { + for (auto const& hash : m_resources.keys()) { if (m_temp_versions.contains(hash)) { auto data = m_temp_versions.find(hash).value(); @@ -461,20 +461,20 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask() auto id = QString::number(Json::requireInteger(entry_obj, "id")); auto hash = addonIds.find(id).value(); - auto mod = m_mods.find(hash).value(); + auto resource = m_resources.find(hash).value(); try { - setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(mod->name())); + setStatus(tr("Parsing API response from CurseForge for '%1'...").arg(resource->name())); ModPlatform::IndexedPack pack; FlameMod::loadIndexedPack(pack, entry_obj); - flameCallback(pack, m_temp_versions.find(hash).value(), mod); + flameCallback(pack, m_temp_versions.find(hash).value(), resource); } catch (Json::JsonException& e) { qDebug() << e.cause(); qDebug() << entries; - emitFail(mod); + emitFail(resource); } } } catch (Json::JsonException& e) { @@ -486,17 +486,17 @@ Task::Ptr EnsureMetadataTask::flameProjectsTask() return proj_task; } -void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod) +void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource) { // Prevent file name mismatch - ver.fileName = mod->fileinfo().fileName(); + ver.fileName = resource->fileinfo().fileName(); if (ver.fileName.endsWith(".disabled")) ver.fileName.chop(9); QDir tmp_index_dir(m_index_dir); { - LocalModUpdateTask update_metadata(m_index_dir, pack, ver); + LocalResourceUpdateTask update_metadata(m_index_dir, pack, ver); QEventLoop loop; QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); @@ -510,27 +510,27 @@ void EnsureMetadataTask::modrinthCallback(ModPlatform::IndexedPack& pack, ModPla auto metadata = Metadata::get(tmp_index_dir, pack.slug); if (!metadata.isValid()) { qCritical() << "Failed to generate metadata at last step!"; - emitFail(mod); + emitFail(resource); return; } - mod->setMetadata(metadata); + resource->setMetadata(metadata); - emitReady(mod); + emitReady(resource); } -void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod* mod) +void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource* resource) { try { // Prevent file name mismatch - ver.fileName = mod->fileinfo().fileName(); + ver.fileName = resource->fileinfo().fileName(); if (ver.fileName.endsWith(".disabled")) ver.fileName.chop(9); QDir tmp_index_dir(m_index_dir); { - LocalModUpdateTask update_metadata(m_index_dir, pack, ver); + LocalResourceUpdateTask update_metadata(m_index_dir, pack, ver); QEventLoop loop; QObject::connect(&update_metadata, &Task::finished, &loop, &QEventLoop::quit); @@ -544,16 +544,16 @@ void EnsureMetadataTask::flameCallback(ModPlatform::IndexedPack& pack, ModPlatfo auto metadata = Metadata::get(tmp_index_dir, pack.slug); if (!metadata.isValid()) { qCritical() << "Failed to generate metadata at last step!"; - emitFail(mod); + emitFail(resource); return; } - mod->setMetadata(metadata); + resource->setMetadata(metadata); - emitReady(mod); + emitReady(resource); } catch (Json::JsonException& e) { qDebug() << e.cause(); - emitFail(mod); + emitFail(resource); } } diff --git a/launcher/modplatform/EnsureMetadataTask.h b/launcher/modplatform/EnsureMetadataTask.h index 2f276e5a0..d82d9c26e 100644 --- a/launcher/modplatform/EnsureMetadataTask.h +++ b/launcher/modplatform/EnsureMetadataTask.h @@ -5,6 +5,7 @@ #include "modplatform/helpers/HashUtils.h" +#include "minecraft/mod/Resource.h" #include "tasks/ConcurrentTask.h" class Mod; @@ -14,8 +15,8 @@ class EnsureMetadataTask : public Task { Q_OBJECT public: - EnsureMetadataTask(Mod*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); - EnsureMetadataTask(QList&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); + EnsureMetadataTask(Resource*, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); + EnsureMetadataTask(QList&, QDir, ModPlatform::ResourceProvider = ModPlatform::ResourceProvider::MODRINTH); ~EnsureMetadataTask() = default; @@ -36,23 +37,23 @@ class EnsureMetadataTask : public Task { // Helpers enum class RemoveFromList { Yes, No }; - void emitReady(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes); - void emitFail(Mod*, QString key = {}, RemoveFromList = RemoveFromList::Yes); + void emitReady(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes); + void emitFail(Resource*, QString key = {}, RemoveFromList = RemoveFromList::Yes); // Hashes and stuff - auto createNewHash(Mod*) -> Hashing::Hasher::Ptr; - auto getExistingHash(Mod*) -> QString; + auto createNewHash(Resource*) -> Hashing::Hasher::Ptr; + auto getExistingHash(Resource*) -> QString; private slots: - void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*); - void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Mod*); + void modrinthCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource*); + void flameCallback(ModPlatform::IndexedPack& pack, ModPlatform::IndexedVersion& ver, Resource*); signals: - void metadataReady(Mod*); - void metadataFailed(Mod*); + void metadataReady(Resource*); + void metadataFailed(Resource*); private: - QHash m_mods; + QHash m_resources; QDir m_index_dir; ModPlatform::ResourceProvider m_provider; diff --git a/launcher/ui/pages/modplatform/ResourcePage.cpp b/launcher/ui/pages/modplatform/ResourcePage.cpp index c087e2be3..648ff7050 100644 --- a/launcher/ui/pages/modplatform/ResourcePage.cpp +++ b/launcher/ui/pages/modplatform/ResourcePage.cpp @@ -48,6 +48,7 @@ #include "minecraft/MinecraftInstance.h" +#include "Application.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include "ui/pages/modplatform/ResourceModel.h" #include "ui/widgets/ProjectItem.h" @@ -332,7 +333,8 @@ void ResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pack, ModPlatform::IndexedVersion& ver, const std::shared_ptr base_model) { - m_model->addPack(pack, ver, base_model); + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); + m_model->addPack(pack, ver, base_model, is_indexed); } void ResourcePage::removeResourceFromPage(const QString& name) diff --git a/launcher/ui/pages/modplatform/ShaderPackPage.cpp b/launcher/ui/pages/modplatform/ShaderPackPage.cpp index 586dffc55..6f4120289 100644 --- a/launcher/ui/pages/modplatform/ShaderPackPage.cpp +++ b/launcher/ui/pages/modplatform/ShaderPackPage.cpp @@ -8,6 +8,7 @@ #include "ShaderPackModel.h" +#include "Application.h" #include "ui/dialogs/ResourceDownloadDialog.h" #include @@ -48,10 +49,11 @@ void ShaderPackResourcePage::addResourceToPage(ModPlatform::IndexedPack::Ptr pac ModPlatform::IndexedVersion& version, const std::shared_ptr base_model) { + bool is_indexed = !APPLICATION->settings()->get("ModMetadataDisabled").toBool(); QString custom_target_folder; if (version.loaders & ModPlatform::Cauldron) custom_target_folder = QStringLiteral("resourcepacks"); - m_model->addPack(pack, version, base_model, false, custom_target_folder); + m_model->addPack(pack, version, base_model, is_indexed, custom_target_folder); } } // namespace ResourceDownload From 179abfa03eff92404bce37388f994ba1692459a9 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 1 Sep 2023 22:05:44 +0100 Subject: [PATCH 05/39] Resource provider column Signed-off-by: TheKodeToad --- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 9 ++++++--- launcher/minecraft/mod/ResourceFolderModel.h | 8 ++++---- .../minecraft/mod/ResourcePackFolderModel.cpp | 14 ++++++++----- .../minecraft/mod/ResourcePackFolderModel.h | 2 +- .../minecraft/mod/TexturePackFolderModel.cpp | 20 ++++++++++--------- .../minecraft/mod/TexturePackFolderModel.h | 2 +- 7 files changed, 33 insertions(+), 24 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 43c64a667..370bddb22 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -160,7 +160,7 @@ QVariant ModFolderModel::headerData(int section, [[maybe_unused]] Qt::Orientatio case DateColumn: return tr("The date and time this mod was last changed (or added)."); case ProviderColumn: - return tr("Where the mod was downloaded from."); + return tr("The source provider of the mod."); default: return QVariant(); } diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 27eb4d2e5..0f155f695 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -466,6 +466,8 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->name(); case DATE_COLUMN: return m_resources[row]->dateTimeChanged(); + case PROVIDER_COLUMN: + return m_resources[row]->provider(); default: return {}; } @@ -535,21 +537,22 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien case ACTIVE_COLUMN: case NAME_COLUMN: case DATE_COLUMN: + case PROVIDER_COLUMN: return columnNames().at(section); default: return {}; } case Qt::ToolTipRole: { + //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. switch (section) { case ACTIVE_COLUMN: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("Is the resource enabled?"); case NAME_COLUMN: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The name of the resource."); case DATE_COLUMN: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. return tr("The date and time this resource was last changed (or added)."); + case PROVIDER_COLUMN: + return tr("The source provider of the resource."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index e8f8d2b9e..7deff5fe9 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -100,7 +100,7 @@ class ResourceFolderModel : public QAbstractListModel { /* Qt behavior */ /* Basic columns */ - enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, NUM_COLUMNS }; + enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, PROVIDER_COLUMN, NUM_COLUMNS }; QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } [[nodiscard]] int rowCount(const QModelIndex& parent = {}) const override { return parent.isValid() ? 0 : static_cast(size()); } @@ -205,11 +205,11 @@ class ResourceFolderModel : public QAbstractListModel { // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; - QStringList m_column_names = { "Enable", "Name", "Last Modified" }; - QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified") }; + QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider" }; + QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider") }; QList m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Stretch, QHeaderView::ResizeToContents }; - QList m_columnsHideable = { false, false, true }; + QList m_columnsHideable = { false, false, true, true }; QDir m_dir; BaseInstance* m_instance; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 2e72e02f2..11175e5a6 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -50,12 +50,12 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified") }); + m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Provider" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true, true }; + m_columnsHideable = { false, true, false, true, true, true }; } QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const @@ -86,7 +86,8 @@ QVariant ResourcePackFolderModel::data(const QModelIndex& index, int role) const } case DateColumn: return m_resources[row]->dateTimeChanged(); - + case ProviderColumn: + return m_resources[row]->provider(); default: return {}; } @@ -140,6 +141,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case PackFormatColumn: case DateColumn: case ImageColumn: + case ProviderColumn: return columnNames().at(section); default: return {}; @@ -148,7 +150,7 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O case Qt::ToolTipRole: switch (section) { case ActiveColumn: - return tr("Is the resource pack enabled? (Only valid for ZIPs)"); + return tr("Is the resource pack enabled?"); case NameColumn: return tr("The name of the resource pack."); case PackFormatColumn: @@ -156,6 +158,8 @@ QVariant ResourcePackFolderModel::headerData(int section, [[maybe_unused]] Qt::O return tr("The resource pack format ID, as well as the Minecraft versions it was designed for."); case DateColumn: return tr("The date and time this resource pack was last changed (or added)."); + case ProviderColumn: + return tr("The source provider of the resource pack."); default: return {}; } diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.h b/launcher/minecraft/mod/ResourcePackFolderModel.h index fd56c8c73..f00acee29 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.h +++ b/launcher/minecraft/mod/ResourcePackFolderModel.h @@ -7,7 +7,7 @@ class ResourcePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, PackFormatColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; explicit ResourcePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 23a392421..a20e2b5db 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -45,12 +45,12 @@ TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent) : ResourceFolderModel(QDir(dir), instance, is_indexed, create_dir, parent) { - m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified") }); + m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Provider" }); + m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Provider") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE }; m_column_resize_modes = { QHeaderView::ResizeToContents, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::ResizeToContents }; - m_columnsHideable = { false, true, false, true }; + m_columnsHideable = { false, true, false, true, true }; } Task* TexturePackFolderModel::createParseTask(Resource& resource) @@ -73,6 +73,8 @@ QVariant TexturePackFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->name(); case DateColumn: return m_resources[row]->dateTimeChanged(); + case ProviderColumn: + return m_resources[row]->provider(); default: return {}; } @@ -119,6 +121,7 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case NameColumn: case DateColumn: case ImageColumn: + case ProviderColumn: return columnNames().at(section); default: return {}; @@ -126,14 +129,13 @@ QVariant TexturePackFolderModel::headerData(int section, [[maybe_unused]] Qt::Or case Qt::ToolTipRole: { switch (section) { case ActiveColumn: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("Is the resource enabled?"); + return tr("Is the texture pack enabled?"); case NameColumn: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The name of the resource."); + return tr("The name of the texture pack."); case DateColumn: - //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. - return tr("The date and time this resource was last changed (or added)."); + return tr("The date and time this texture pack was last changed (or added)."); + case ProviderColumn: + return tr("The source provider of the texture pack."); default: return {}; } diff --git a/launcher/minecraft/mod/TexturePackFolderModel.h b/launcher/minecraft/mod/TexturePackFolderModel.h index 44b19b57a..649842e23 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.h +++ b/launcher/minecraft/mod/TexturePackFolderModel.h @@ -44,7 +44,7 @@ class TexturePackFolderModel : public ResourceFolderModel { Q_OBJECT public: - enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, ImageColumn, NameColumn, DateColumn, ProviderColumn, NUM_COLUMNS }; explicit TexturePackFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); From 2c18d0f1a511c23898bb8f82d57c99458712bc30 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 25 Jan 2024 15:41:34 +0000 Subject: [PATCH 06/39] Store current version in packwiz metadata (temporarily using `x-prismlauncher-version-number`) Signed-off-by: TheKodeToad --- launcher/modplatform/packwiz/Packwiz.cpp | 9 ++++++--- launcher/modplatform/packwiz/Packwiz.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index d8d496af7..f68624c1a 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -91,8 +91,9 @@ auto intEntry(toml::table table, QString entry_name) -> int return node.value_or(0); } -auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) - -> Mod +auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, + ModPlatform::IndexedPack& mod_pack, + ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -111,6 +112,7 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, ModPlatform::In mod.hash = mod_version.hash; mod.provider = mod_pack.provider; + mod.version_number = mod_version.version_number; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; mod.side = stringToSide(mod_pack.side); @@ -199,7 +201,8 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) { "hash-format", mod.hash_format.toStdString() }, { "hash", mod.hash.toStdString() }, } }, - { "update", toml::table{ { ProviderCaps.name(mod.provider), update } } } }; + { "update", toml::table{ { ProviderCaps.name(mod.provider), update }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() } } } }; std::stringstream ss; ss << tbl; in_stream << QString::fromStdString(ss.str()); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 97c8f4b16..07bb4248d 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -52,6 +52,7 @@ class V1 { // [update] ModPlatform::ResourceProvider provider{}; + QString version_number{}; QVariant file_id{}; QVariant project_id{}; From 97ee0a19b5caf1714d033b282b5fca1bc70d6bff Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 26 Jan 2024 02:53:30 +0000 Subject: [PATCH 07/39] Refactor updating mechanisms to work with all resources Summary: - It compiles - I need to go to bed Signed-off-by: TheKodeToad --- launcher/CMakeLists.txt | 4 +- launcher/minecraft/mod/ModFolderModel.cpp | 39 +---- launcher/minecraft/mod/ModFolderModel.h | 3 - launcher/minecraft/mod/Resource.cpp | 2 +- launcher/minecraft/mod/Resource.h | 2 +- .../minecraft/mod/ResourceFolderModel.cpp | 6 +- launcher/minecraft/mod/ResourceFolderModel.h | 89 ++++++---- .../minecraft/mod/ResourcePackFolderModel.cpp | 18 +- .../minecraft/mod/TexturePackFolderModel.cpp | 15 +- launcher/modplatform/CheckUpdateTask.h | 34 ++-- .../modplatform/flame/FlameCheckUpdate.cpp | 63 ++++--- launcher/modplatform/flame/FlameCheckUpdate.h | 6 +- .../modrinth/ModrinthCheckUpdate.cpp | 84 ++++----- .../modrinth/ModrinthCheckUpdate.h | 6 +- launcher/modplatform/packwiz/Packwiz.cpp | 10 +- ...ateDialog.cpp => ResourceUpdateDialog.cpp} | 164 +++++++++--------- ...dUpdateDialog.h => ResourceUpdateDialog.h} | 33 ++-- launcher/ui/pages/instance/ModFolderPage.cpp | 13 +- 18 files changed, 298 insertions(+), 293 deletions(-) rename launcher/ui/dialogs/{ModUpdateDialog.cpp => ResourceUpdateDialog.cpp} (74%) rename launcher/ui/dialogs/{ModUpdateDialog.h => ResourceUpdateDialog.h} (53%) 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()) { From 82d0f204e2293e8969cc6604c1dff0d601a47b4d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 26 Jan 2024 03:01:02 +0000 Subject: [PATCH 08/39] De-OOP ProviderCapabilities There was no reason for it to be a class, and imo it created quite a code-smell needing to initialise it everywhere. Signed-off-by: TheKodeToad --- launcher/minecraft/mod/Resource.cpp | 4 +--- launcher/modplatform/EnsureMetadataTask.cpp | 8 +++----- launcher/modplatform/ModIndex.h | 3 +-- launcher/modplatform/flame/FlameModIndex.cpp | 3 +-- launcher/modplatform/helpers/HashUtils.cpp | 14 ++++++-------- .../modplatform/modrinth/ModrinthCheckUpdate.cpp | 3 +-- .../modplatform/modrinth/ModrinthPackIndex.cpp | 3 +-- launcher/modplatform/packwiz/Packwiz.cpp | 8 +++----- launcher/ui/dialogs/ChooseProviderDialog.cpp | 4 +--- launcher/ui/dialogs/ResourceDownloadDialog.cpp | 4 +--- launcher/ui/dialogs/ResourceUpdateDialog.cpp | 4 +--- 11 files changed, 20 insertions(+), 38 deletions(-) diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 30b453812..7c1fb9108 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -5,8 +5,6 @@ #include "FileSystem.h" -static ModPlatform::ProviderCapabilities ProviderCaps; - Resource::Resource(QObject* parent) : QObject(parent) {} Resource::Resource(QFileInfo file_info) : QObject() @@ -74,7 +72,7 @@ static void removeThePrefix(QString& string) auto Resource::provider() const -> QString { if (metadata()) - return ProviderCaps.readableName(metadata()->provider); + return ModPlatform::ProviderCapabilities::readableName(metadata()->provider); return tr("Unknown"); } diff --git a/launcher/modplatform/EnsureMetadataTask.cpp b/launcher/modplatform/EnsureMetadataTask.cpp index b71bb1e0e..277cd0764 100644 --- a/launcher/modplatform/EnsureMetadataTask.cpp +++ b/launcher/modplatform/EnsureMetadataTask.cpp @@ -15,8 +15,6 @@ #include "modplatform/modrinth/ModrinthAPI.h" #include "modplatform/modrinth/ModrinthPackIndex.h" -static ModPlatform::ProviderCapabilities ProviderCaps; - static ModrinthAPI modrinth_api; static FlameAPI flame_api; @@ -162,10 +160,10 @@ void EnsureMetadataTask::executeTask() }); if (m_resources.size() > 1) - setStatus(tr("Requesting metadata information from %1...").arg(ProviderCaps.readableName(m_provider))); + setStatus(tr("Requesting metadata information from %1...").arg(ModPlatform::ProviderCapabilities::readableName(m_provider))); else if (!m_resources.empty()) setStatus(tr("Requesting metadata information from %1 for '%2'...") - .arg(ProviderCaps.readableName(m_provider), m_resources.begin().value()->name())); + .arg(ModPlatform::ProviderCapabilities::readableName(m_provider), m_resources.begin().value()->name())); m_current_task = version_task; version_task->start(); @@ -215,7 +213,7 @@ void EnsureMetadataTask::emitFail(Resource* resource, QString key, RemoveFromLis Task::Ptr EnsureMetadataTask::modrinthVersionsTask() { - auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); + auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first(); auto response = std::make_shared(); auto ver_task = modrinth_api.currentVersions(m_resources.keys(), hash_type, response); diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index eff7e7f9f..aeae87235 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -40,8 +40,7 @@ enum class ResourceType { MOD, RESOURCE_PACK, SHADER_PACK }; enum class DependencyType { REQUIRED, OPTIONAL, INCOMPATIBLE, EMBEDDED, TOOL, INCLUDE, UNKNOWN }; -class ProviderCapabilities { - public: +namespace ProviderCapabilities { auto name(ResourceProvider) -> const char*; auto readableName(ResourceProvider) -> QString; auto hashType(ResourceProvider) -> QStringList; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 345883c17..16cbbade4 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -6,7 +6,6 @@ #include "modplatform/flame/FlameAPI.h" static FlameAPI api; -static ModPlatform::ProviderCapabilities ProviderCaps; void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { @@ -158,7 +157,7 @@ auto FlameMod::loadIndexedPackVersion(QJsonObject& obj, bool load_changelog) -> auto hash_list = Json::ensureArray(obj, "hashes"); for (auto h : hash_list) { auto hash_entry = Json::ensureObject(h); - auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::FLAME); + auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::FLAME); auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm")); if (hash_types.contains(hash_algo)) { file.hash = Json::requireString(hash_entry, "value"); diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index 6ff1d1710..19e5b447a 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -10,8 +10,6 @@ namespace Hashing { -static ModPlatform::ProviderCapabilities ProviderCaps; - Hasher::Ptr createHasher(QString file_path, ModPlatform::ResourceProvider provider) { switch (provider) { @@ -62,8 +60,8 @@ void ModrinthHasher::executeTask() return; } - auto hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); - m_hash = ProviderCaps.hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type); + auto hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first(); + m_hash = ModPlatform::ProviderCapabilities::hash(ModPlatform::ResourceProvider::MODRINTH, &file, hash_type); file.close(); @@ -96,7 +94,7 @@ void FlameHasher::executeTask() BlockedModHasher::BlockedModHasher(QString file_path, ModPlatform::ResourceProvider provider) : Hasher(file_path), provider(provider) { setObjectName(QString("BlockedModHasher: %1").arg(file_path)); - hash_type = ProviderCaps.hashType(provider).first(); + hash_type = ModPlatform::ProviderCapabilities::hashType(provider).first(); } void BlockedModHasher::executeTask() @@ -113,7 +111,7 @@ void BlockedModHasher::executeTask() return; } - m_hash = ProviderCaps.hash(provider, &file, hash_type); + m_hash = ModPlatform::ProviderCapabilities::hash(provider, &file, hash_type); file.close(); @@ -127,12 +125,12 @@ void BlockedModHasher::executeTask() QStringList BlockedModHasher::getHashTypes() { - return ProviderCaps.hashType(provider); + return ModPlatform::ProviderCapabilities::hashType(provider); } bool BlockedModHasher::useHashType(QString type) { - auto types = ProviderCaps.hashType(provider); + auto types = ModPlatform::ProviderCapabilities::hashType(provider); if (types.contains(type)) { hash_type = type; return true; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index f7e9fb8a9..881f5499c 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -13,7 +13,6 @@ #include "minecraft/mod/ModFolderModel.h" static ModrinthAPI api; -static ModPlatform::ProviderCapabilities ProviderCaps; bool ModrinthCheckUpdate::abort() { @@ -36,7 +35,7 @@ void ModrinthCheckUpdate::executeTask() // Create all hashes QStringList hashes; - auto best_hash_type = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH).first(); + auto best_hash_type = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first(); ConcurrentTask hashing_task(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); for (auto* resource : m_resources) { diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index c1c30ab5f..7a74619e5 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -25,7 +25,6 @@ #include "modplatform/ModIndex.h" static ModrinthAPI api; -static ModPlatform::ProviderCapabilities ProviderCaps; bool shouldDownloadOnSide(QString side) { @@ -233,7 +232,7 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject& obj, QString preferred_hash_t file.hash = Json::requireString(hash_list, preferred_hash_type); file.hash_type = preferred_hash_type; } else { - auto hash_types = ProviderCaps.hashType(ModPlatform::ResourceProvider::MODRINTH); + auto hash_types = ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH); for (auto& hash_type : hash_types) { if (hash_list.contains(hash_type)) { file.hash = Json::requireString(hash_list, hash_type); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 81c0a1efb..c609e78fc 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -66,8 +66,6 @@ static inline auto indexFileName(QString const& mod_slug) -> QString return QString("%1.pw.toml").arg(mod_slug); } -static ModPlatform::ProviderCapabilities ProviderCaps; - // Helper functions for extracting data from the TOML file auto stringEntry(toml::table table, QString entry_name) -> QString { @@ -201,7 +199,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) { "hash-format", mod.hash_format.toStdString() }, { "hash", mod.hash.toStdString() }, } }, - { "update", toml::table{ { ProviderCaps.name(mod.provider), update }, + { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update }, { "x-prismlauncher-version-number", mod.version_number.toStdString() } } } }; std::stringstream ss; ss << tbl; @@ -306,11 +304,11 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod 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)[ModPlatform::ProviderCapabilities::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)[ModPlatform::ProviderCapabilities::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/ChooseProviderDialog.cpp b/launcher/ui/dialogs/ChooseProviderDialog.cpp index 83748e1e2..68457802d 100644 --- a/launcher/ui/dialogs/ChooseProviderDialog.cpp +++ b/launcher/ui/dialogs/ChooseProviderDialog.cpp @@ -6,8 +6,6 @@ #include "modplatform/ModIndex.h" -static ModPlatform::ProviderCapabilities ProviderCaps; - ChooseProviderDialog::ChooseProviderDialog(QWidget* parent, bool single_choice, bool allow_skipping) : QDialog(parent), ui(new Ui::ChooseProviderDialog) { @@ -78,7 +76,7 @@ void ChooseProviderDialog::addProviders() QRadioButton* btn; for (auto& provider : { ModPlatform::ResourceProvider::MODRINTH, ModPlatform::ResourceProvider::FLAME }) { - btn = new QRadioButton(ProviderCaps.readableName(provider), this); + btn = new QRadioButton(ModPlatform::ProviderCapabilities::readableName(provider), this); m_providers.addButton(btn, btn_index++); ui->providersLayout->addWidget(btn); } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 1431ea92c..3f2be760a 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -125,8 +125,6 @@ void ResourceDownloadDialog::connectButtons() connect(HelpButton, &QPushButton::clicked, m_container, &PageContainer::help); } -static ModPlatform::ProviderCapabilities ProviderCaps; - void ResourceDownloadDialog::confirm() { auto confirm_dialog = ReviewMessageBox::create(this, tr("Confirm %1 to download").arg(resourcesString())); @@ -167,7 +165,7 @@ void ResourceDownloadDialog::confirm() }); for (auto& task : selected) { confirm_dialog->appendResource({ task->getName(), task->getFilename(), task->getCustomPath(), - ProviderCaps.name(task->getProvider()), getRequiredBy.value(task->getPack()->addonId.toString()), + ModPlatform::ProviderCapabilities::name(task->getProvider()), getRequiredBy.value(task->getPack()->addonId.toString()), task->getVersion().version_type.toString() }); } diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.cpp b/launcher/ui/dialogs/ResourceUpdateDialog.cpp index cad5b9f7f..459ba7f23 100644 --- a/launcher/ui/dialogs/ResourceUpdateDialog.cpp +++ b/launcher/ui/dialogs/ResourceUpdateDialog.cpp @@ -24,8 +24,6 @@ #include -static ModPlatform::ProviderCapabilities ProviderCaps; - static std::list mcVersions(BaseInstance* inst) { return { static_cast(inst)->getPackProfile()->getComponent("net.minecraft")->getVersion() }; @@ -423,7 +421,7 @@ void ResourceUpdateDialog::appendResource(CheckUpdateTask::Update const& info, Q item_top->setExpanded(true); auto provider_item = new QTreeWidgetItem(item_top); - provider_item->setText(0, tr("Provider: %1").arg(ProviderCaps.readableName(info.provider))); + provider_item->setText(0, tr("Provider: %1").arg(ModPlatform::ProviderCapabilities::readableName(info.provider))); auto old_version_item = new QTreeWidgetItem(item_top); old_version_item->setText(0, tr("Old version: %1").arg(info.old_version)); From 27780cc7aeee5fd1a5bd535d721c642491cca192 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 6 Mar 2024 14:56:04 +0000 Subject: [PATCH 09/39] Add update UI for all resource types Signed-off-by: TheKodeToad --- launcher/LaunchController.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- .../minecraft/mod/ResourcePackFolderModel.cpp | 5 +- .../minecraft/mod/ShaderPackFolderModel.h | 2 + launcher/modplatform/ResourceAPI.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 28 ++-- launcher/modplatform/packwiz/Packwiz.h | 2 +- launcher/ui/dialogs/ResourceUpdateDialog.cpp | 16 +- launcher/ui/dialogs/ResourceUpdateDialog.h | 4 +- .../pages/instance/ExternalResourcesPage.ui | 50 +++--- launcher/ui/pages/instance/ModFolderPage.cpp | 153 ++++++------------ launcher/ui/pages/instance/ModFolderPage.h | 7 +- .../ui/pages/instance/ResourcePackPage.cpp | 128 ++++++++++++++- launcher/ui/pages/instance/ResourcePackPage.h | 9 +- launcher/ui/pages/instance/ShaderPackPage.cpp | 130 ++++++++++++++- launcher/ui/pages/instance/ShaderPackPage.h | 7 +- .../ui/pages/instance/TexturePackPage.cpp | 128 ++++++++++++++- launcher/ui/pages/instance/TexturePackPage.h | 7 +- 18 files changed, 498 insertions(+), 184 deletions(-) diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index a30f99439..ff8558ce7 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -316,7 +316,7 @@ void LaunchController::launchInstance() online_mode = "online"; // Prepend Server Status - QStringList servers = { "authserver.mojang.com", "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; + QStringList servers = { "session.minecraft.net", "textures.minecraft.net", "api.mojang.com" }; QString resolved_servers = ""; QHostInfo host_info; diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3de3c5476..e65c69b25 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -255,7 +255,7 @@ class ResourceFolderModel : public QAbstractListModel { protected: // Represents the relationship between a column's index (represented by the list index), and it's sorting key. // As such, the order in with they appear is very important! - QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE }; + QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider") }; QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index b777dbafd..ccbf80367 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -51,8 +51,9 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* : ResourceFolderModel(dir, instance, is_indexed, create_dir, parent) { m_column_names = QStringList({ "Enable", "Image", "Name", "Pack Format", "Last Modified", "Provider" }); - m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE }; + m_column_names_translated = + QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider") }); + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::PROVIDER }; m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true }; diff --git a/launcher/minecraft/mod/ShaderPackFolderModel.h b/launcher/minecraft/mod/ShaderPackFolderModel.h index c63def238..cd01f6226 100644 --- a/launcher/minecraft/mod/ShaderPackFolderModel.h +++ b/launcher/minecraft/mod/ShaderPackFolderModel.h @@ -20,4 +20,6 @@ class ShaderPackFolderModel : public ResourceFolderModel { { return new LocalShaderPackParseTask(m_next_resolution_ticket, static_cast(resource)); } + + RESOURCE_HELPERS(ShaderPack); }; diff --git a/launcher/modplatform/ResourceAPI.h b/launcher/modplatform/ResourceAPI.h index 2c7bec5d4..7b787c6a0 100644 --- a/launcher/modplatform/ResourceAPI.h +++ b/launcher/modplatform/ResourceAPI.h @@ -83,7 +83,7 @@ class ResourceAPI { struct VersionSearchArgs { ModPlatform::IndexedPack pack; - std::optional > mcVersions; + std::optional> mcVersions; std::optional loaders; VersionSearchArgs(VersionSearchArgs const&) = default; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index c609e78fc..6e1f507fd 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -110,11 +110,14 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, mod.hash = mod_version.hash; mod.provider = mod_pack.provider; - mod.version_number = mod_version.version_number; mod.file_id = mod_version.fileId; mod.project_id = mod_pack.addonId; mod.side = stringToSide(mod_pack.side); + mod.version_number = mod_version.version_number; + if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a versio + mod.version_number = mod_version.version; + return mod; } @@ -164,10 +167,9 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); return; } - update = toml::table{ - { "file-id", mod.file_id.toInt() }, - { "project-id", mod.project_id.toInt() }, - }; + update = toml::table{ { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() } }; break; case (ModPlatform::ResourceProvider::MODRINTH): if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { @@ -177,6 +179,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) update = toml::table{ { "mod-id", mod.mod_id().toString().toStdString() }, { "version", mod.version().toString().toStdString() }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() }, }; break; } @@ -199,8 +202,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) { "hash-format", mod.hash_format.toStdString() }, { "hash", mod.hash.toStdString() }, } }, - { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update }, - { "x-prismlauncher-version-number", mod.version_number.toStdString() } } } }; + { "update", toml::table{ { ModPlatform::ProviderCapabilities::name(mod.provider), update } } } }; std::stringstream ss; ss << tbl; in_stream << QString::fromStdString(ss.str()); @@ -295,23 +297,23 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod { // [update] info using Provider = ModPlatform::ResourceProvider; - auto update_table = table["update"].as_table(); - if (!update_table) { + auto update_table = table["update"]; + if (!update_table || !update_table.is_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)[ModPlatform::ProviderCapabilities::name(Provider::FLAME)].as_table())) { + if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::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)[ModPlatform::ProviderCapabilities::name(Provider::MODRINTH)].as_table())) { + mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); + } else if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::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"); + mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 07bb4248d..19ef22dd8 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -52,9 +52,9 @@ class V1 { // [update] ModPlatform::ResourceProvider provider{}; - QString version_number{}; QVariant file_id{}; QVariant project_id{}; + QString version_number{}; public: // This is a totally heuristic, but should work for now. diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.cpp b/launcher/ui/dialogs/ResourceUpdateDialog.cpp index 459ba7f23..6426c2bfd 100644 --- a/launcher/ui/dialogs/ResourceUpdateDialog.cpp +++ b/launcher/ui/dialogs/ResourceUpdateDialog.cpp @@ -38,7 +38,8 @@ ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, BaseInstance* instance, const std::shared_ptr resource_model, QList& search_for, - bool includeDeps) + bool include_deps, + bool filter_loaders) : ReviewMessageBox(parent, tr("Confirm mods to update"), "") , m_parent(parent) , m_resource_model(resource_model) @@ -46,7 +47,8 @@ ResourceUpdateDialog::ResourceUpdateDialog(QWidget* parent, , m_second_try_metadata( new ConcurrentTask(nullptr, "Second Metadata Search", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())) , m_instance(instance) - , m_include_deps(includeDeps) + , m_include_deps(include_deps) + , m_filter_loaders(filter_loaders) { ReviewMessageBox::setGeometry(0, 0, 800, 600); @@ -85,7 +87,7 @@ void ResourceUpdateDialog::checkCandidates() } auto versions = mcVersions(m_instance); - auto loaders = mcLoaders(m_instance); + auto loaders = m_filter_loaders ? mcLoaders(m_instance) : std::optional(); SequentialTask check_task(m_parent, tr("Checking for updates")); @@ -224,10 +226,10 @@ void ResourceUpdateDialog::checkCandidates() 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 - }; + 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 }; appendResource(updatable, getRequiredBy.value(dep->version.addonId.toString())); m_tasks.insert(updatable.name, updatable.download); diff --git a/launcher/ui/dialogs/ResourceUpdateDialog.h b/launcher/ui/dialogs/ResourceUpdateDialog.h index 31aab8f5d..de1d845d2 100644 --- a/launcher/ui/dialogs/ResourceUpdateDialog.h +++ b/launcher/ui/dialogs/ResourceUpdateDialog.h @@ -20,7 +20,8 @@ class ResourceUpdateDialog final : public ReviewMessageBox { BaseInstance* instance, std::shared_ptr resource_model, QList& search_for, - bool includeDeps); + bool include_deps, + bool filter_loaders); void checkCandidates(); @@ -63,4 +64,5 @@ class ResourceUpdateDialog final : public ReviewMessageBox { bool m_no_updates = false; bool m_aborted = false; bool m_include_deps = false; + bool m_filter_loaders = false; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index ff08e12d2..2b4a47b9d 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -70,15 +70,15 @@ - - true - Actions Qt::ToolButtonTextOnly + + true + RightToolBarArea @@ -95,10 +95,10 @@ - &Add + &Add File - Add + Add a locally downloaded file. @@ -106,7 +106,7 @@ &Remove - Remove selected item + Remove all selected items. @@ -114,7 +114,7 @@ &Enable - Enable selected item + Enable all selected items. @@ -122,7 +122,7 @@ &Disable - Disable selected item + Disable all selected items. @@ -137,6 +137,9 @@ View &Folder + + Open the folder in the system file manager. + @@ -146,18 +149,7 @@ &Download - Download a new resource - - - - - false - - - Visit mod's page - - - Go to mods home page + Download resources from online mod platforms. @@ -168,7 +160,23 @@ Check for &Updates - Try to check or update all selected resources (all resources if none are selected) + Try to check or update all selected resources (all resources if none are selected). + + + + + Reset Update Metadata + + + QAction::NoRole + + + + + Verify Dependencies + + + QAction::NoRole diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index e513cb1fa..5b10a38df 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -68,88 +68,37 @@ #include "tasks/ConcurrentTask.h" #include "ui/dialogs/ProgressDialog.h" -ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) - : ExternalResourcesPage(inst, mods, parent), m_model(mods) +ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr model, QWidget* parent) + : ExternalResourcesPage(inst, model, parent), m_model(model) { - // This is structured like that so that these changes - // do not affect the Resource pack and Shader pack tabs - { - ui->actionDownloadItem->setText(tr("Download mods")); - ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); - ui->actionDownloadItem->setEnabled(true); - ui->actionAddItem->setText(tr("Add file")); - ui->actionAddItem->setToolTip(tr("Add a locally downloaded file")); + ui->actionDownloadItem->setText(tr("Download Mods")); + ui->actionDownloadItem->setToolTip(tr("Download mods from online mod platforms")); + ui->actionDownloadItem->setEnabled(true); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::downloadMods); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ModFolderPage::installMods); + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); - // update menu - auto updateMenu = ui->actionUpdateItem->menu(); - if (updateMenu) { - updateMenu->clear(); - } else { - updateMenu = new QMenu(this); - } + auto updateMenu = new QMenu(this); - auto update = updateMenu->addAction(tr("Check for Updates")); - update->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); - connect(update, &QAction::triggered, this, &ModFolderPage::updateMods); + auto update = updateMenu->addAction(tr("Check for Updates")); + connect(update, &QAction::triggered, this, &ModFolderPage::updateMods); - auto updateWithDeps = updateMenu->addAction(tr("Verify Dependencies")); - updateWithDeps->setToolTip( - tr("Try to update and check for missing dependencies all selected mods (all mods if none are selected)")); - connect(updateWithDeps, &QAction::triggered, this, [this] { updateMods(true); }); + updateMenu->addAction(ui->actionVerifyItemDependencies); + connect(ui->actionVerifyItemDependencies, &QAction::triggered, this, [this] { updateMods(true); }); - auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); - updateWithDeps->setVisible(!depsDisabled->get().toBool()); - connect(depsDisabled.get(), &Setting::SettingChanged, this, - [updateWithDeps](const Setting& setting, QVariant value) { updateWithDeps->setVisible(!value.toBool()); }); + auto depsDisabled = APPLICATION->settings()->getSetting("ModDependenciesDisabled"); + ui->actionVerifyItemDependencies->setVisible(!depsDisabled->get().toBool()); + connect(depsDisabled.get(), &Setting::SettingChanged, this, + [this](const Setting& setting, const QVariant& value) { ui->actionVerifyItemDependencies->setVisible(!value.toBool()); }); - auto actionRemoveItemMetadata = updateMenu->addAction(tr("Reset update metadata")); - actionRemoveItemMetadata->setToolTip(tr("Remove mod's metadata")); - connect(actionRemoveItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); - actionRemoveItemMetadata->setEnabled(false); + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ModFolderPage::deleteModMetadata); - ui->actionUpdateItem->setMenu(updateMenu); - - ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected mods (all mods if none are selected)")); - connect(ui->actionUpdateItem, &QAction::triggered, this, &ModFolderPage::updateMods); - ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); - - ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - ui->actionsToolbar->addAction(ui->actionVisitItemPage); - connect(ui->actionVisitItemPage, &QAction::triggered, this, &ModFolderPage::visitModPages); - - auto check_allow_update = [this] { return ui->treeView->selectionModel()->hasSelection() || !m_model->empty(); }; - - connect(ui->treeView->selectionModel(), &QItemSelectionModel::selectionChanged, this, - [this, check_allow_update, actionRemoveItemMetadata] { - ui->actionUpdateItem->setEnabled(check_allow_update()); - - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto mods_list = m_model->selectedMods(selection); - auto selected = std::count_if(mods_list.cbegin(), mods_list.cend(), - [](Mod* v) { return v->metadata() != nullptr || v->homeurl().size() != 0; }); - if (selected <= 1) { - ui->actionVisitItemPage->setText(tr("Visit mod's page")); - ui->actionVisitItemPage->setToolTip(tr("Go to mod's home page")); - - } else { - ui->actionVisitItemPage->setText(tr("Visit mods' pages")); - ui->actionVisitItemPage->setToolTip(tr("Go to the pages of the selected mods")); - } - ui->actionVisitItemPage->setEnabled(selected != 0); - actionRemoveItemMetadata->setEnabled(selected != 0); - }); - - auto updateButtons = [this, check_allow_update] { ui->actionUpdateItem->setEnabled(check_allow_update()); }; - connect(mods.get(), &ModFolderModel::rowsInserted, this, updateButtons); - - connect(mods.get(), &ModFolderModel::rowsRemoved, this, updateButtons); - - connect(mods.get(), &ModFolderModel::updateFinished, this, updateButtons); - } + ui->actionUpdateItem->setMenu(updateMenu); } bool ModFolderPage::shouldDisplay() const @@ -182,7 +131,7 @@ void ModFolderPage::removeItems(const QItemSelection& selection) m_model->deleteResources(selection.indexes()); } -void ModFolderPage::installMods() +void ModFolderPage::downloadMods() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance @@ -195,7 +144,7 @@ void ModFolderPage::installMods() ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = new ConcurrentTask(this, tr("Download Mods"), APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -257,7 +206,7 @@ void ModFolderPage::updateMods(bool includeDeps) if (use_all) mods_list = m_model->allResources(); - ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps); + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, includeDeps, true); update_dialog.checkCandidates(); if (update_dialog.aborted()) { @@ -307,6 +256,27 @@ void ModFolderPage::updateMods(bool includeDeps) } } +void ModFolderPage::deleteModMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedMods(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 mods.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} + CoreModFolderPage::CoreModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent) : ModFolderPage(inst, mods, parent) {} @@ -340,34 +310,3 @@ bool NilModFolderPage::shouldDisplay() const { return m_model->dir().exists(); } - -void ModFolderPage::visitModPages() -{ - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - for (auto mod : m_model->selectedMods(selection)) { - auto url = mod->metaurl(); - if (!url.isEmpty()) - DesktopServices::openUrl(url); - } -} - -void ModFolderPage::deleteModMetadata() -{ - auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - auto selectionCount = m_model->selectedMods(selection).length(); - if (selectionCount == 0) - return; - if (selectionCount > 1) { - auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), - tr("You are about to remove the metadata for %1 mods.\n" - "Are you sure?") - .arg(selectionCount), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); - - if (response != QMessageBox::Yes) - return; - } - - m_model->deleteMetadata(selection); -} diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 4672350c6..455db33cb 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -44,7 +44,7 @@ class ModFolderPage : public ExternalResourcesPage { Q_OBJECT public: - explicit ModFolderPage(BaseInstance* inst, std::shared_ptr mods, QWidget* parent = nullptr); + explicit ModFolderPage(BaseInstance* inst, std::shared_ptr model, QWidget* parent = nullptr); virtual ~ModFolderPage() = default; void setFilter(const QString& filter) { m_fileSelectionFilter = filter; } @@ -61,11 +61,10 @@ class ModFolderPage : public ExternalResourcesPage { private slots: void removeItems(const QItemSelection& selection) override; - void deleteModMetadata(); - void installMods(); + void downloadMods(); void updateMods(bool includeDeps = false); - void visitModPages(); + void deleteModMetadata(); protected: std::shared_ptr m_model; diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index 85be64256..e51ebafac 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -42,17 +42,31 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download packs")); - ui->actionDownloadItem->setToolTip(tr("Download resource packs from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download resource packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadRPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ResourcePackPage::downloadResourcePacks); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected resource packs (all resource packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &ResourcePackPage::updateResourcePacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ResourcePackPage::deleteResourcePackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -65,12 +79,12 @@ bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_un return true; } -void ResourcePackPage::downloadRPs() +void ResourcePackPage::downloadResourcePacks() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::ResourcePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::ResourcePackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { auto tasks = new ConcurrentTask(this, "Download Resource Pack", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); @@ -101,3 +115,103 @@ void ResourcePackPage::downloadRPs() m_model->update(); } } + +void ResourcePackPage::updateResourcePacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Resource pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating resource packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The resource pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All resource packs are up-to-date! :)"); + } else { + message = tr("All selected resource packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void ResourcePackPage::deleteResourcePackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedResourcePacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 resource packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index cb84ca96d..c2d36cea1 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -59,5 +59,12 @@ class ResourcePackPage : public ExternalResourcesPage { public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; - void downloadRPs(); + + private slots: + void downloadResourcePacks(); + void updateResourcePacks(); + void deleteResourcePackMetadata(); + + protected: + std::shared_ptr m_model; }; diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp index 40366a1be..768d7c710 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.cpp +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -45,27 +45,41 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download shaders")); - ui->actionDownloadItem->setToolTip(tr("Download shaders from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download shader packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaders); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &ShaderPackPage::downloadShaderPack); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected shader packs (all shader packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &ShaderPackPage::updateShaderPacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ShaderPackPage::deleteShaderPackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } -void ShaderPackPage::downloadShaders() +void ShaderPackPage::downloadShaderPack() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::ShaderPackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::ShaderPackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { - auto tasks = new ConcurrentTask(this, "Download Shaders", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); @@ -93,3 +107,103 @@ void ShaderPackPage::downloadShaders() m_model->update(); } } + +void ShaderPackPage::updateShaderPacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Shader pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating shader packs while the game is running may pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The shader pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All shader packs are up-to-date! :)"); + } else { + message = tr("All selected shader packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void ShaderPackPage::deleteShaderPackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedShaderPacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 shader packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index 7c43a3756..fadffe82d 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -53,5 +53,10 @@ class ShaderPackPage : public ExternalResourcesPage { bool shouldDisplay() const override { return true; } public slots: - void downloadShaders(); + void downloadShaderPack(); + void updateShaderPacks(); + void deleteShaderPackMetadata(); + + private: + std::shared_ptr m_model; }; diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 7c8d7e061..75f8410bc 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -44,17 +44,31 @@ #include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/ProgressDialog.h" #include "ui/dialogs/ResourceDownloadDialog.h" +#include "ui/dialogs/ResourceUpdateDialog.h" TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptr model, QWidget* parent) - : ExternalResourcesPage(instance, model, parent) + : ExternalResourcesPage(instance, model, parent), m_model(model) { - ui->actionDownloadItem->setText(tr("Download packs")); - ui->actionDownloadItem->setToolTip(tr("Download texture packs from online platforms")); + ui->actionDownloadItem->setText(tr("Download Packs")); + ui->actionDownloadItem->setToolTip(tr("Download texture packs from online mod platforms")); ui->actionDownloadItem->setEnabled(true); - connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTPs); ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionDownloadItem); - ui->actionViewConfigs->setVisible(false); + connect(ui->actionDownloadItem, &QAction::triggered, this, &TexturePackPage::downloadTexturePacks); + + ui->actionUpdateItem->setToolTip(tr("Try to check or update all selected texture packs (all texture packs if none are selected)")); + connect(ui->actionUpdateItem, &QAction::triggered, this, &TexturePackPage::updateTexturePacks); + ui->actionsToolbar->insertActionBefore(ui->actionAddItem, ui->actionUpdateItem); + + auto updateMenu = new QMenu(this); + + auto update = updateMenu->addAction(ui->actionUpdateItem->text()); + connect(update, &QAction::triggered, this, &TexturePackPage::updateTexturePacks); + + updateMenu->addAction(ui->actionResetItemMetadata); + connect(ui->actionResetItemMetadata, &QAction::triggered, this, &TexturePackPage::deleteTexturePackMetadata); + + ui->actionUpdateItem->setMenu(updateMenu); } bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -67,12 +81,12 @@ bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unu return true; } -void TexturePackPage::downloadTPs() +void TexturePackPage::downloadTexturePacks() { if (m_instance->typeName() != "Minecraft") return; // this is a null instance or a legacy instance - ResourceDownload::TexturePackDownloadDialog mdownload(this, std::static_pointer_cast(m_model), m_instance); + ResourceDownload::TexturePackDownloadDialog mdownload(this, m_model, m_instance); if (mdownload.exec()) { auto tasks = new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); @@ -103,3 +117,103 @@ void TexturePackPage::downloadTPs() m_model->update(); } } + +void TexturePackPage::updateTexturePacks() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + auto profile = static_cast(m_instance)->getPackProfile(); + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Texture pack updates are unavailable when metadata is disabled!")); + return; + } + if (m_instance != nullptr && m_instance->isRunning()) { + auto response = + CustomMessageBox::selectable(this, tr("Confirm Update"), + tr("Updating texture packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + + auto mods_list = m_model->selectedResources(selection); + bool use_all = mods_list.empty(); + if (use_all) + mods_list = m_model->allResources(); + + ResourceUpdateDialog update_dialog(this, m_instance, m_model, mods_list, false, false); + update_dialog.checkCandidates(); + + if (update_dialog.aborted()) { + CustomMessageBox::selectable(this, tr("Aborted"), tr("The texture pack updater was aborted!"), QMessageBox::Warning)->show(); + return; + } + if (update_dialog.noUpdates()) { + QString message{ tr("'%1' is up-to-date! :)").arg(mods_list.front()->name()) }; + if (mods_list.size() > 1) { + if (use_all) { + message = tr("All texture packs are up-to-date! :)"); + } else { + message = tr("All selected texture packs are up-to-date! :)"); + } + } + CustomMessageBox::selectable(this, tr("Update checker"), message)->exec(); + return; + } + + if (update_dialog.exec()) { + auto tasks = new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) { + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } + tasks->deleteLater(); + }); + + for (auto task : update_dialog.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} + +void TexturePackPage::deleteTexturePackMetadata() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + auto selectionCount = m_model->selectedTexturePacks(selection).length(); + if (selectionCount == 0) + return; + if (selectionCount > 1) { + auto response = CustomMessageBox::selectable(this, tr("Confirm Removal"), + tr("You are about to remove the metadata for %1 texture packs.\n" + "Are you sure?") + .arg(selectionCount), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); + + if (response != QMessageBox::Yes) + return; + } + + m_model->deleteMetadata(selection); +} diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 9c4f24b70..e42613568 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -56,5 +56,10 @@ class TexturePackPage : public ExternalResourcesPage { public slots: bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; - void downloadTPs(); + void downloadTexturePacks(); + void updateTexturePacks(); + void deleteTexturePackMetadata(); + + private: + std::shared_ptr m_model; }; From 803e26a4018d788178032b653fd06d1b8970b3a4 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 26 Apr 2024 01:40:18 +0100 Subject: [PATCH 10/39] Make actions more consistent Signed-off-by: TheKodeToad --- .../pages/instance/ExternalResourcesPage.cpp | 34 +++++++++++++------ .../ui/pages/instance/ExternalResourcesPage.h | 5 ++- .../pages/instance/ExternalResourcesPage.ui | 11 +++++- launcher/ui/pages/instance/ModFolderPage.cpp | 4 +-- launcher/ui/pages/instance/ModFolderPage.h | 2 +- .../ui/pages/instance/ResourcePackPage.cpp | 21 ++++++------ launcher/ui/pages/instance/ResourcePackPage.h | 2 +- .../ui/pages/instance/TexturePackPage.cpp | 21 ++++++------ launcher/ui/pages/instance/TexturePackPage.h | 2 +- 9 files changed, 59 insertions(+), 43 deletions(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 2068fa6b1..a3b58f4df 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -81,15 +81,28 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared connect(ui->treeView, &ModListView::activated, this, &ExternalResourcesPage::itemActivated); auto selection_model = ui->treeView->selectionModel(); - connect(selection_model, &QItemSelectionModel::currentChanged, this, &ExternalResourcesPage::current); + + connect(selection_model, &QItemSelectionModel::currentChanged, this, [this](const QModelIndex& current, const QModelIndex& previous) { + if (!current.isValid()) { + ui->frame->clear(); + return; + } + + updateFrame(current, previous); + }); + auto updateExtra = [this]() { if (updateExtraInfo) updateExtraInfo(id(), extraHeaderInfoString()); }; + connect(selection_model, &QItemSelectionModel::selectionChanged, this, updateExtra); connect(model.get(), &ResourceFolderModel::updateFinished, this, updateExtra); - connect(ui->filterEdit, &QLineEdit::textChanged, this, &ExternalResourcesPage::filterTextChanged); + connect(selection_model, &QItemSelectionModel::selectionChanged, this, [this] { updateActions(); }); + connect(m_model.get(), &ResourceFolderModel::rowsInserted, this, [this] { updateActions(); }); + connect(m_model.get(), &ResourceFolderModel::rowsRemoved, this, [this] { updateActions(); }); + connect(m_model.get(), &ResourceFolderModel::updateFinished, this, [this] { updateActions(); }); auto viewHeader = ui->treeView->header(); viewHeader->setContextMenuPolicy(Qt::CustomContextMenu); @@ -298,23 +311,22 @@ void ExternalResourcesPage::viewFolder() DesktopServices::openPath(m_model->dir().absolutePath(), true); } -bool ExternalResourcesPage::current(const QModelIndex& current, const QModelIndex& previous) +void ExternalResourcesPage::updateActions() { - if (!current.isValid()) { - ui->frame->clear(); - return false; - } - - return onSelectionChanged(current, previous); + const bool hasSelection = ui->treeView->selectionModel()->hasSelection(); + ui->actionUpdateItem->setEnabled(!m_model->empty()); + ui->actionResetItemMetadata->setEnabled(hasSelection); + ui->actionRemoveItem->setEnabled(hasSelection); + ui->actionEnableItem->setEnabled(hasSelection); + ui->actionDisableItem->setEnabled(hasSelection); } -bool ExternalResourcesPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) +void ExternalResourcesPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); Resource const& resource = m_model->at(row); ui->frame->updateWithResource(resource); - return true; } QString ExternalResourcesPage::extraHeaderInfoString() diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index d29be0fc3..9bbd13984 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -42,9 +42,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { QMenu* createPopupMenu() override; public slots: - bool current(const QModelIndex& current, const QModelIndex& previous); - - virtual bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous); + virtual void updateActions(); + virtual void updateFrame(const QModelIndex& current, const QModelIndex& previous); protected slots: void itemActivated(const QModelIndex& index); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 2b4a47b9d..c671efaf8 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -102,6 +102,9 @@ + + false + &Remove @@ -110,6 +113,9 @@ + + false + &Enable @@ -118,6 +124,9 @@ + + false + &Disable @@ -154,7 +163,7 @@ - true + false Check for &Updates diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 5b10a38df..e647120c2 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -106,14 +106,12 @@ bool ModFolderPage::shouldDisplay() const return true; } -bool ModFolderPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) +void ModFolderPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); const Mod& mod = m_model->at(row); ui->frame->updateWithMod(mod); - - return true; } void ModFolderPage::removeItems(const QItemSelection& selection) diff --git a/launcher/ui/pages/instance/ModFolderPage.h b/launcher/ui/pages/instance/ModFolderPage.h index 455db33cb..4fac80141 100644 --- a/launcher/ui/pages/instance/ModFolderPage.h +++ b/launcher/ui/pages/instance/ModFolderPage.h @@ -57,7 +57,7 @@ class ModFolderPage : public ExternalResourcesPage { virtual bool shouldDisplay() const override; public slots: - bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; + void updateFrame(const QModelIndex& current, const QModelIndex& previous) override; private slots: void removeItems(const QItemSelection& selection) override; diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index e51ebafac..d03511ca4 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -69,14 +69,12 @@ ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr< ui->actionUpdateItem->setMenu(updateMenu); } -bool ResourcePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) +void ResourcePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); auto& rp = static_cast(m_model->at(row)); ui->frame->updateWithResourcePack(rp); - - return true; } void ResourcePackPage::downloadResourcePacks() @@ -127,13 +125,13 @@ void ResourcePackPage::updateResourcePacks() return; } if (m_instance != nullptr && m_instance->isRunning()) { - auto response = - CustomMessageBox::selectable(this, tr("Confirm Update"), - tr("Updating resource packs while the game is running may cause pack duplication and game crashes.\n" - "The old files may not be deleted as they are in use.\n" - "Are you sure you want to do this?"), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + auto response = CustomMessageBox::selectable( + this, tr("Confirm Update"), + tr("Updating resource packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); if (response != QMessageBox::Yes) return; @@ -166,7 +164,8 @@ void ResourcePackPage::updateResourcePacks() } if (update_dialog.exec()) { - auto tasks = new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = + new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index c2d36cea1..e95809cf8 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -58,7 +58,7 @@ class ResourcePackPage : public ExternalResourcesPage { } public slots: - bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; + void updateFrame(const QModelIndex& current, const QModelIndex& previous) override; private slots: void downloadResourcePacks(); diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 75f8410bc..cdd2b0964 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -71,14 +71,12 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptractionUpdateItem->setMenu(updateMenu); } -bool TexturePackPage::onSelectionChanged(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) +void TexturePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) { auto sourceCurrent = m_filterModel->mapToSource(current); int row = sourceCurrent.row(); auto& rp = static_cast(m_model->at(row)); ui->frame->updateWithTexturePack(rp); - - return true; } void TexturePackPage::downloadTexturePacks() @@ -129,13 +127,13 @@ void TexturePackPage::updateTexturePacks() return; } if (m_instance != nullptr && m_instance->isRunning()) { - auto response = - CustomMessageBox::selectable(this, tr("Confirm Update"), - tr("Updating texture packs while the game is running may cause pack duplication and game crashes.\n" - "The old files may not be deleted as they are in use.\n" - "Are you sure you want to do this?"), - QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) - ->exec(); + auto response = CustomMessageBox::selectable( + this, tr("Confirm Update"), + tr("Updating texture packs while the game is running may cause pack duplication and game crashes.\n" + "The old files may not be deleted as they are in use.\n" + "Are you sure you want to do this?"), + QMessageBox::Warning, QMessageBox::Yes | QMessageBox::No, QMessageBox::No) + ->exec(); if (response != QMessageBox::Yes) return; @@ -168,7 +166,8 @@ void TexturePackPage::updateTexturePacks() } if (update_dialog.exec()) { - auto tasks = new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + auto tasks = + new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); tasks->deleteLater(); diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index e42613568..4dc2c16c2 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -55,7 +55,7 @@ class TexturePackPage : public ExternalResourcesPage { virtual bool shouldDisplay() const override { return m_instance->traits().contains("texturepacks"); } public slots: - bool onSelectionChanged(const QModelIndex& current, const QModelIndex& previous) override; + void updateFrame(const QModelIndex& current, const QModelIndex& previous) override; void downloadTexturePacks(); void updateTexturePacks(); void deleteTexturePackMetadata(); From 9ce10231edcf256eb0bcb2a3e78c3c06b156c800 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 26 Apr 2024 01:44:43 +0100 Subject: [PATCH 11/39] Remove redundant event handler Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index a3b58f4df..1ee473541 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -102,7 +102,6 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared connect(selection_model, &QItemSelectionModel::selectionChanged, this, [this] { updateActions(); }); connect(m_model.get(), &ResourceFolderModel::rowsInserted, this, [this] { updateActions(); }); connect(m_model.get(), &ResourceFolderModel::rowsRemoved, this, [this] { updateActions(); }); - connect(m_model.get(), &ResourceFolderModel::updateFinished, this, [this] { updateActions(); }); auto viewHeader = ui->treeView->header(); viewHeader->setContextMenuPolicy(Qt::CustomContextMenu); From a76f37760cb043749e15582afda349caf67e330c Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 8 Oct 2024 22:30:50 +0100 Subject: [PATCH 12/39] Fix log spam when opening files from older versions Signed-off-by: TheKodeToad --- launcher/modplatform/packwiz/Packwiz.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 19929f173..7e7411b03 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -299,7 +299,7 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = stringToSide(stringEntry(table, "side")); - mod.releaseType = ModPlatform::IndexedVersionType(stringEntry(table, "x-prismlauncher-release-type")); + mod.releaseType = ModPlatform::IndexedVersionType(table["x-prismlauncher-release-type"].value_or("")); if (auto loaders = table["x-prismlauncher-loaders"]; loaders && loaders.is_array()) { for (auto&& loader : *loaders.as_array()) { if (loader.is_string()) { @@ -352,7 +352,10 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod mod.provider = Provider::MODRINTH; mod.mod_id() = stringEntry(*mod_provider_table, "mod-id"); mod.version() = stringEntry(*mod_provider_table, "version"); - mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); + + auto version_number_node = (*mod_provider_table)["x-prismlauncher-version-number"]; + if (version_number_node) + mod.version_number = version_number_node.value_or(""); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; From 4b66320f63031b63090f42bc164b62f82ddfc1df Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 8 Oct 2024 22:51:03 +0100 Subject: [PATCH 13/39] Move x-prism-launcher to unnamed table for consistency Signed-off-by: TheKodeToad --- launcher/modplatform/packwiz/Packwiz.cpp | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 7e7411b03..fe5ef34e7 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -120,7 +120,7 @@ auto V1::createModFormat([[maybe_unused]] const QDir& index_dir, mod.releaseType = mod_version.version_type; mod.version_number = mod_version.version_number; - if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a version number + if (mod.version_number.isNull()) // on CurseForge, there is only a version name - not a version number mod.version_number = mod_version.version; return mod; @@ -172,9 +172,10 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) qCritical() << QString("Did not write file %1 because missing information!").arg(normalized_fname); return; } - update = toml::table{ { "file-id", mod.file_id.toInt() }, - { "project-id", mod.project_id.toInt() }, - { "x-prismlauncher-version-number", mod.version_number.toStdString() } }; + update = toml::table{ + { "file-id", mod.file_id.toInt() }, + { "project-id", mod.project_id.toInt() }, + }; break; case (ModPlatform::ResourceProvider::MODRINTH): if (mod.mod_id().toString().isEmpty() || mod.version().toString().isEmpty()) { @@ -184,7 +185,6 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) update = toml::table{ { "mod-id", mod.mod_id().toString().toStdString() }, { "version", mod.version().toString().toStdString() }, - { "x-prismlauncher-version-number", mod.version_number.toStdString() }, }; break; } @@ -215,6 +215,7 @@ void V1::updateModIndex(const QDir& index_dir, Mod& mod) { "x-prismlauncher-loaders", loaders }, { "x-prismlauncher-mc-versions", mcVersions }, { "x-prismlauncher-release-type", mod.releaseType.toString().toStdString() }, + { "x-prismlauncher-version-number", mod.version_number.toStdString() }, { "download", toml::table{ { "mode", mod.mode.toStdString() }, @@ -319,6 +320,7 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod mod.mcVersions.sort(); } } + mod.version_number = table["x-prismlauncher-version-number"].value_or(""); { // [download] info auto download_table = table["download"].as_table(); @@ -347,15 +349,10 @@ auto V1::getIndexForMod(const QDir& index_dir, QString slug) -> Mod mod.provider = Provider::FLAME; mod.file_id = intEntry(*mod_provider_table, "file-id"); mod.project_id = intEntry(*mod_provider_table, "project-id"); - mod.version_number = stringEntry(*mod_provider_table, "x-prismlauncher-version-number"); } else if ((mod_provider_table = update_table[ModPlatform::ProviderCapabilities::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"); - - auto version_number_node = (*mod_provider_table)["x-prismlauncher-version-number"]; - if (version_number_node) - mod.version_number = version_number_node.value_or(""); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; From f13e875f0d06c02803409dbb7be375691838d09d Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 22 Oct 2024 18:46:10 +0100 Subject: [PATCH 14/39] Try to fix ModrinthCheckUpdate (not working) Signed-off-by: TheKodeToad --- launcher/modplatform/CheckUpdateTask.h | 3 +- launcher/modplatform/flame/FlameCheckUpdate.h | 2 +- .../modrinth/ModrinthCheckUpdate.cpp | 109 ++++++++---------- .../modrinth/ModrinthCheckUpdate.h | 14 +-- 4 files changed, 59 insertions(+), 69 deletions(-) diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 1ffcc97ce..540998287 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -21,7 +21,8 @@ class CheckUpdateTask : public Task { , m_resources(resources) , m_game_versions(mcVersions) , m_loaders_list(std::move(loadersList)) - , m_resource_model(resourceModel){}; + , m_resource_model(std::move(resourceModel)) + {} struct Update { QString name; diff --git a/launcher/modplatform/flame/FlameCheckUpdate.h b/launcher/modplatform/flame/FlameCheckUpdate.h index 0094bb13a..2b5c6ac17 100644 --- a/launcher/modplatform/flame/FlameCheckUpdate.h +++ b/launcher/modplatform/flame/FlameCheckUpdate.h @@ -12,7 +12,7 @@ class FlameCheckUpdate : public CheckUpdateTask { std::list& mcVersions, QList loadersList, std::shared_ptr resourceModel) - : CheckUpdateTask(resources, mcVersions, loadersList, resourceModel) + : CheckUpdateTask(resources, mcVersions, std::move(loadersList), std::move(resourceModel)) {} public slots: diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index c004bd8f2..f77c5598a 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -29,7 +29,7 @@ bool ModrinthCheckUpdate::abort() void ModrinthCheckUpdate::executeTask() { setStatus(tr("Preparing resources for Modrinth...")); - setProgress(0, 9); + setProgress(0, (m_loaders_list.isEmpty() ? 1 : m_loaders_list.length()) * 2 + 1); auto hashing_task = makeShared(this, "MakeModrinthHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt()); @@ -54,10 +54,30 @@ void ModrinthCheckUpdate::executeTask() hashing_task->start(); } -void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr response, - ModPlatform::ModLoaderTypes loader, - bool forceModLoaderCheck) +void ModrinthCheckUpdate::getUpdateModsForLoader(std::optional loader) { + setStatus(tr("Waiting for the API response from Modrinth...")); + setProgress(m_progress + 1, m_progressTotal); + + auto response = std::make_shared(); + QStringList hashes = m_mappings.keys(); + auto job = api.latestVersions(hashes, m_hash_type, m_game_versions, loader, response); + + connect(job.get(), &Task::succeeded, this, + [this, response, loader] { checkVersionsResponse(response, loader); }); + + connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::checkNextLoader); + + m_job = job; + job->start(); +} + +void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr response, + std::optional loader) +{ + setStatus(tr("Parsing the API response from Modrinth...")); + setProgress(m_progress + 1, m_progressTotal); + QJsonParseError parse_error{}; QJsonDocument doc = QJsonDocument::fromJson(*response, &parse_error); if (parse_error.error != QJsonParseError::NoError) { @@ -69,17 +89,11 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp return; } - setStatus(tr("Parsing the API response from Modrinth...")); - setProgress(m_next_loader_idx * 2, 9); - try { for (auto iter = m_mappings.begin(); iter != m_mappings.end(); iter++) { const QString& hash = iter.key(); Resource* resource = iter.value(); - if (forceModLoaderCheck && !(resource->metadata()->loaders & loader)) - continue; - auto project_obj = doc[hash].toObject(); // If the returned project is empty, but we have Modrinth metadata, @@ -97,7 +111,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge, ModPlatform::ModLoaderType::Quilt, ModPlatform::ModLoaderType::Fabric }; for (auto flag : flags) { - if (loader.testFlag(flag)) { + if (loader.has_value() && loader->testFlag(flag)) { loader_filter = ModPlatform::getModLoaderAsString(flag); break; } @@ -153,63 +167,38 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp checkNextLoader(); } -void ModrinthCheckUpdate::getUpdateModsForLoader(ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck) -{ - auto response = std::make_shared(); - QStringList hashes; - if (forceModLoaderCheck) { - for (auto hash : m_mappings.keys()) { - if (m_mappings[hash]->metadata()->loaders & loader) { - hashes.append(hash); - } - } - } else { - hashes = m_mappings.keys(); - } - auto job = api.latestVersions(hashes, m_hash_type, m_game_versions, loader, response); - - connect(job.get(), &Task::succeeded, this, - [this, response, loader, forceModLoaderCheck] { checkVersionsResponse(response, loader, forceModLoaderCheck); }); - - connect(job.get(), &Task::failed, this, &ModrinthCheckUpdate::checkNextLoader); - - setStatus(tr("Waiting for the API response from Modrinth...")); - setProgress(m_next_loader_idx * 2 - 1, 9); - - m_job = job; - job->start(); -} - void ModrinthCheckUpdate::checkNextLoader() { if (m_mappings.isEmpty()) { emitSucceeded(); return; } - if (m_next_loader_idx < m_loaders_list.size()) { - getUpdateModsForLoader(m_loaders_list.at(m_next_loader_idx)); - m_next_loader_idx++; - return; - } - static auto flags = { ModPlatform::ModLoaderType::NeoForge, ModPlatform::ModLoaderType::Forge, ModPlatform::ModLoaderType::Quilt, - ModPlatform::ModLoaderType::Fabric }; - for (auto flag : flags) { - if (!m_loaders_list.contains(flag)) { - m_loaders_list.append(flag); - m_next_loader_idx++; - setProgress(m_next_loader_idx * 2 - 1, 9); - for (auto resource : m_mappings) { - if (resource->metadata()->loaders & flag) { - getUpdateModsForLoader(flag, true); - return; - } - } - setProgress(m_next_loader_idx * 2, 9); + + if (m_loaders_list.size() == 0) { + if (m_loader_idx == 0) { + getUpdateModsForLoader({}); + m_loader_idx++; + return; } } - for (auto m : m_mappings) { - emit checkFailed(m, - tr("No valid version found for this mod. It's probably unavailable for the current game version / mod loader.")); + if (m_loader_idx < m_loaders_list.size()) { + getUpdateModsForLoader(m_loaders_list.at(m_loader_idx)); + m_loader_idx++; + return; } + + for (auto resource : m_mappings) { + 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); + } + emitSucceeded(); } diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h index 0bbd21321..204b24784 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.h +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.h @@ -7,10 +7,10 @@ class ModrinthCheckUpdate : public CheckUpdateTask { public: ModrinthCheckUpdate(QList& resources, - std::list& mcVersions, - QList loadersList, - std::shared_ptr resourceModel) - : CheckUpdateTask(resources, mcVersions, loadersList, resourceModel) + std::list& mcVersions, + QList loadersList, + std::shared_ptr resourceModel) + : CheckUpdateTask(resources, mcVersions, std::move(loadersList), std::move(resourceModel)) , m_hash_type(ModPlatform::ProviderCapabilities::hashType(ModPlatform::ResourceProvider::MODRINTH).first()) {} @@ -19,13 +19,13 @@ class ModrinthCheckUpdate : public CheckUpdateTask { protected slots: void executeTask() override; - void getUpdateModsForLoader(ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck = false); - void checkVersionsResponse(std::shared_ptr response, ModPlatform::ModLoaderTypes loader, bool forceModLoaderCheck = false); + void getUpdateModsForLoader(std::optional loader); + void checkVersionsResponse(std::shared_ptr response, std::optional loader); void checkNextLoader(); private: Task::Ptr m_job = nullptr; QHash m_mappings; QString m_hash_type; - int m_next_loader_idx = 0; + int m_loader_idx = 0; }; From 24cecf7b63f91766d1b941c3b2f8e7e5d0971f36 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 23 Oct 2024 14:42:54 +0100 Subject: [PATCH 15/39] Fix compile Signed-off-by: TheKodeToad --- .../modplatform/modrinth/ModrinthInstanceCreationTask.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index f34fccfc5..4494f9371 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -303,8 +303,8 @@ bool ModrinthCreationTask::createInstance() loop.exec(); if (!ended_well) { - for (auto m : mods) { - delete m; + for (auto resource : resources) { + delete resource; } return ended_well; } @@ -324,8 +324,8 @@ bool ModrinthCreationTask::createInstance() m_task = ensureMetadataTask; ensureMetaLoop.exec(); - for (auto m : resources) { - delete m; + for (auto resource : resources) { + delete resource; } resources.clear(); From 92a906e0d499e5d93f1796246aa3630ae5f1bbd0 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Wed, 23 Oct 2024 14:58:52 +0100 Subject: [PATCH 16/39] Move everything in CheckUpdateTask::Update Signed-off-by: TheKodeToad --- launcher/modplatform/CheckUpdateTask.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 540998287..9d0b1c51a 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -45,14 +45,14 @@ class CheckUpdateTask : public Task { ModPlatform::ResourceProvider p, shared_qobject_ptr t, bool enabled = true) - : name(name) - , old_hash(old_h) - , old_version(old_v) - , new_version(new_v) - , new_version_type(new_v_type) - , changelog(changelog) + : name(std::move(name)) + , old_hash(std::move(old_h)) + , old_version(std::move(old_v)) + , new_version(std::move(new_v)) + , new_version_type(std::move(new_v_type)) + , changelog(std::move(changelog)) , provider(p) - , download(t) + , download(std::move(t)) , enabled(enabled) {} }; From 8e586c883c5da83f1c91404625f5030f6985920e Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 24 Oct 2024 01:16:06 +0100 Subject: [PATCH 17/39] Fix Modrinth update crashing Signed-off-by: TheKodeToad --- .../modrinth/ModrinthCheckUpdate.cpp | 34 ++++++++----------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index f77c5598a..e38ca526f 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -63,8 +63,7 @@ void ModrinthCheckUpdate::getUpdateModsForLoader(std::optionalstart(); } -void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr response, - std::optional loader) +void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr response, std::optional loader) { setStatus(tr("Parsing the API response from Modrinth...")); setProgress(m_progress + 1, m_progressTotal); @@ -90,8 +88,10 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp } try { - for (auto iter = m_mappings.begin(); iter != m_mappings.end(); iter++) { - const QString& hash = iter.key(); + auto iter = m_mappings.begin(); + + while (iter != m_mappings.end()) { + const QString hash = iter.key(); Resource* resource = iter.value(); auto project_obj = doc[hash].toObject(); @@ -101,7 +101,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp if (project_obj.isEmpty()) { qDebug() << "Mod " << m_mappings.find(hash).value()->name() << " got an empty response." << "Hash: " << hash; - + ++iter; continue; } @@ -126,23 +126,15 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp auto project_ver = Modrinth::loadIndexedPackVersion(project_obj, m_hash_type, loader_filter); if (project_ver.downloadUrl.isEmpty()) { qCritical() << "Modrinth mod without download url!" << project_ver.fileName; - + ++iter; continue; } - auto mod_iter = m_mappings.find(hash); - if (mod_iter == m_mappings.end()) { - qCritical() << "Failed to remap mod from Modrinth!"; - continue; - } - auto mod = *mod_iter; - m_mappings.remove(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->name = resource->name(); + pack->slug = resource->metadata()->slug; + pack->addonId = resource->metadata()->project_id; pack->provider = ModPlatform::ResourceProvider::MODRINTH; if ((project_ver.hash != hash && project_ver.is_preferred) || (resource->status() == ResourceStatus::NOT_INSTALLED)) { auto download_task = makeShared(pack, project_ver, m_resource_model); @@ -159,6 +151,8 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task); } m_deps.append(std::make_shared(pack, project_ver)); + + iter = m_mappings.erase(iter); } } catch (Json::JsonException& e) { emitFailed(e.cause() + ": " + e.what()); @@ -184,7 +178,7 @@ void ModrinthCheckUpdate::checkNextLoader() if (m_loader_idx < m_loaders_list.size()) { getUpdateModsForLoader(m_loaders_list.at(m_loader_idx)); m_loader_idx++; - return; + return; } for (auto resource : m_mappings) { From 5eff9b093451f7c9248832cb9712d5a5fdac5b22 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Thu, 24 Oct 2024 01:23:11 +0100 Subject: [PATCH 18/39] Fix column crashes Signed-off-by: TheKodeToad --- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index c3ce822c8..a92648de9 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -256,7 +256,7 @@ class ResourceFolderModel : public QAbstractListModel { QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider", "Size" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") }; - QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, + QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true, true, true }; QList m_columnsHiddenByDefault = { false, false, false, false, true }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index d41420c5f..84c34582b 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -55,7 +55,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider"), tr("Size") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; - m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true }; } From 13e13ea8fcda9ae8c25b90f697b9f19172eb56cd Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Fri, 25 Oct 2024 19:13:41 +0100 Subject: [PATCH 19/39] Use non-mod metadata in ModrinthPackExportTask Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 14 +- launcher/minecraft/MinecraftInstance.h | 1 + launcher/modplatform/helpers/HashUtils.cpp | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 179 ++++++++++-------- .../modrinth/ModrinthPackExportTask.h | 13 +- launcher/ui/MainWindow.cpp | 27 ++- launcher/ui/dialogs/ExportPackDialog.cpp | 11 +- launcher/ui/dialogs/ExportPackDialog.h | 5 +- 8 files changed, 143 insertions(+), 109 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 2b24ec090..87bd1b703 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -522,7 +522,8 @@ QStringList MinecraftInstance::javaArguments() if (javaVersion.isModular() && shouldApplyOnlineFixes()) // allow reflective access to java.net - required by the skin fix - args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; + args << "--add-opens" + << "java.base/java.net=ALL-UNNAMED"; return args; } @@ -796,8 +797,10 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftT QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { QStringList out; - out << "Main Class:" << " " + getMainClass() << ""; - out << "Native path:" << " " + getNativePath() << ""; + out << "Main Class:" + << " " + getMainClass() << ""; + out << "Native path:" + << " " + getNativePath() << ""; auto profile = m_components->getProfile(); @@ -1227,6 +1230,11 @@ std::shared_ptr MinecraftInstance::shaderPackList() return m_shader_pack_list; } +QList> MinecraftInstance::resourceLists() +{ + return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList() }; +} + std::shared_ptr MinecraftInstance::worldList() { if (!m_world_list) { diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 75e97ae45..5d9bb45ef 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -116,6 +116,7 @@ class MinecraftInstance : public BaseInstance { std::shared_ptr resourcePackList(); std::shared_ptr texturePackList(); std::shared_ptr shaderPackList(); + QList> resourceLists(); std::shared_ptr worldList(); std::shared_ptr gameOptionsModel(); diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index a3b8d904c..1db7ca453 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -115,7 +115,7 @@ QString hash(QIODevice* device, Algorithm type) QCryptographicHash hash(alg); if (!hash.addData(device)) - qCritical() << "Failed to read JAR to create hash!"; + qCritical() << "Failed to read file to create hash!"; Q_ASSERT(hash.result().length() == hash.hashLength(alg)); auto result = hash.result().toHex(); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index d103170af..8df6405ad 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -34,22 +34,21 @@ const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); -ModrinthPackExportTask::ModrinthPackExportTask(const QString& name, - const QString& version, - const QString& summary, +ModrinthPackExportTask::ModrinthPackExportTask(QString name, + QString version, + QString summary, bool optionalFiles, - InstancePtr instance, - const QString& output, + MinecraftInstancePtr instance, + QString output, MMCZip::FilterFunction filter) - : name(name) - , version(version) - , summary(summary) + : name(std::move(name)) + , version(std::move(version)) + , summary(std::move(summary)) , optionalFiles(optionalFiles) - , instance(instance) - , mcInstance(dynamic_cast(instance.get())) - , gameRoot(instance->gameRoot()) - , output(output) - , filter(filter) + , instance(std::move(instance)) + , gameRoot(this->instance->gameRoot()) + , output(std::move(output)) + , filter(std::move(filter)) {} void ModrinthPackExportTask::executeTask() @@ -83,20 +82,86 @@ void ModrinthPackExportTask::collectFiles() pendingHashes.clear(); resolvedFiles.clear(); - if (mcInstance) { - mcInstance->loaderModList()->update(); - connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &ModrinthPackExportTask::collectHashes); - } else - collectHashes(); + collectHashes(); } void ModrinthPackExportTask::collectHashes() { setStatus(tr("Finding file hashes...")); + + for (const auto& model : instance->resourceLists()) { + QCoreApplication::processEvents(); // TODO: maybe don't do this? + + QEventLoop loop; + connect(model.get(), &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); + model->update(); + loop.exec(); + + for (const Resource* resource : model->allResources()) { + QCoreApplication::processEvents(); + + if (resource->metadata() == nullptr) + continue; + + const QUrl& url = resource->metadata()->url; + + if (url.isEmpty() || !BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) + continue; + + const QFileInfo& fileInfo = resource->fileinfo(); + const QString relativePath = gameRoot.relativeFilePath(fileInfo.absoluteFilePath()); + + if (filter(relativePath)) + continue; + + qDebug() << "Resolving" << relativePath << "from index"; + + QString sha1; + QString sha512; + qint64 size; + + if (resource->metadata()->hash_format == "sha1") + sha1 = resource->metadata()->hash; + else if (resource->metadata()->hash_format == "sha512") + sha512 = resource->metadata()->hash; + + { + QFile file(fileInfo.absoluteFilePath()); + + if (!file.open(QFile::ReadOnly)) { + qWarning() << "Could not open" << relativePath << "for hashing"; + continue; + } + + const QByteArray data = file.readAll(); + + if (file.error() != QFileDevice::NoError) { + qWarning() << "Could not read" << relativePath; + continue; + } + + if (sha1.isEmpty()) + sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1); + + if (sha512.isEmpty()) + sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512); + + size = file.size(); + } + + ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), size, resource->metadata()->side }; + resolvedFiles[relativePath] = resolvedFile; + } + } + for (const QFileInfo& file : files) { QCoreApplication::processEvents(); const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); + + if (resolvedFiles.contains(relative)) + continue; + // require sensible file types if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) continue; @@ -105,42 +170,8 @@ void ModrinthPackExportTask::collectHashes() })) continue; - QFile openFile(file.absoluteFilePath()); - if (!openFile.open(QFile::ReadOnly)) { - qWarning() << "Could not open" << file << "for hashing"; - continue; - } - - const QByteArray data = openFile.readAll(); - if (openFile.error() != QFileDevice::NoError) { - qWarning() << "Could not read" << file; - continue; - } - auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512); - - auto allMods = mcInstance->loaderModList()->allMods(); - if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; }); - modIter != allMods.end()) { - const Mod* mod = *modIter; - if (mod->metadata() != nullptr) { - const QUrl& url = mod->metadata()->url; - // ensure the url is permitted on modrinth.com - if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) { - qDebug() << "Resolving" << relative << "from index"; - - auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1); - - ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side }; - resolvedFiles[relative] = resolvedFile; - - // nice! we've managed to resolve based on local metadata! - // no need to enqueue it - continue; - } - } - } - qDebug() << "Enqueueing" << relative << "for Modrinth query"; + auto sha512 = Hashing::hash(file.absoluteFilePath(), Hashing::Algorithm::Sha512); pendingHashes[relative] = sha512; } @@ -241,30 +272,28 @@ QByteArray ModrinthPackExportTask::generateIndex() if (!summary.isEmpty()) out["summary"] = summary; - if (mcInstance) { - auto profile = mcInstance->getPackProfile(); - // collect all supported components - const ComponentPtr minecraft = profile->getComponent("net.minecraft"); - const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); - const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); - const ComponentPtr forge = profile->getComponent("net.minecraftforge"); - const ComponentPtr neoForge = profile->getComponent("net.neoforged"); + auto profile = instance->getPackProfile(); + // collect all supported components + const ComponentPtr minecraft = profile->getComponent("net.minecraft"); + const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); + const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); + const ComponentPtr forge = profile->getComponent("net.minecraftforge"); + const ComponentPtr neoForge = profile->getComponent("net.neoforged"); - // convert all available components to mrpack dependencies - QJsonObject dependencies; - if (minecraft != nullptr) - dependencies["minecraft"] = minecraft->m_version; - if (quilt != nullptr) - dependencies["quilt-loader"] = quilt->m_version; - if (fabric != nullptr) - dependencies["fabric-loader"] = fabric->m_version; - if (forge != nullptr) - dependencies["forge"] = forge->m_version; - if (neoForge != nullptr) - dependencies["neoforge"] = neoForge->m_version; + // convert all available components to mrpack dependencies + QJsonObject dependencies; + if (minecraft != nullptr) + dependencies["minecraft"] = minecraft->m_version; + if (quilt != nullptr) + dependencies["quilt-loader"] = quilt->m_version; + if (fabric != nullptr) + dependencies["fabric-loader"] = fabric->m_version; + if (forge != nullptr) + dependencies["forge"] = forge->m_version; + if (neoForge != nullptr) + dependencies["neoforge"] = neoForge->m_version; - out["dependencies"] = dependencies; - } + out["dependencies"] = dependencies; QJsonArray filesOut; for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) { diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index ee740a456..14bc36009 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -29,12 +29,12 @@ class ModrinthPackExportTask : public Task { Q_OBJECT public: - ModrinthPackExportTask(const QString& name, - const QString& version, - const QString& summary, + ModrinthPackExportTask(QString name, + QString version, + QString summary, bool optionalFiles, - InstancePtr instance, - const QString& output, + MinecraftInstancePtr instance, + QString output, MMCZip::FilterFunction filter); protected: @@ -54,8 +54,7 @@ class ModrinthPackExportTask : public Task { // inputs const QString name, version, summary; const bool optionalFiles; - const InstancePtr instance; - MinecraftInstance* mcInstance; + const MinecraftInstancePtr instance; const QDir gameRoot; const QString output; const MMCZip::FilterFunction filter; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 96f1348d2..6134d9d31 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1403,27 +1403,26 @@ void MainWindow::on_actionExportInstanceZip_triggered() void MainWindow::on_actionExportInstanceMrPack_triggered() { - if (m_selectedInstance) { - ExportPackDialog dlg(m_selectedInstance, this); + auto instance = std::dynamic_pointer_cast(m_selectedInstance); + if (instance) { + ExportPackDialog dlg(std::move(instance), this); dlg.exec(); } } void MainWindow::on_actionExportInstanceFlamePack_triggered() { - if (m_selectedInstance) { - auto instance = dynamic_cast(m_selectedInstance.get()); - if (instance) { - if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); - cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { - QMessageBox msgBox(this); - msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); - msgBox.exec(); - return; - } - ExportPackDialog dlg(m_selectedInstance, this, ModPlatform::ResourceProvider::FLAME); - dlg.exec(); + auto instance = std::dynamic_pointer_cast(m_selectedInstance); + if (instance) { + if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); + cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { + QMessageBox msgBox(this); + msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); + msgBox.exec(); + return; } + ExportPackDialog dlg(std::move(instance), this, ModPlatform::ResourceProvider::FLAME); + dlg.exec(); } } diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 0278c6cb0..c01b83f5c 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -34,7 +34,7 @@ #include "MMCZip.h" #include "modplatform/modrinth/ModrinthPackExportTask.h" -ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider) +ExportPackDialog::ExportPackDialog(const MinecraftInstancePtr& instance, QWidget* parent, ModPlatform::ResourceProvider provider) : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider) { Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME); @@ -86,12 +86,9 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla proxy->blockedPaths().insert(file); } - MinecraftInstance* mcInstance = dynamic_cast(instance.get()); - if (mcInstance) { - const QDir index = mcInstance->loaderModList()->indexDir(); - if (index.exists()) - proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath())); - } + for (auto& resourceModel : instance->resourceLists()) + if (resourceModel->indexDir().exists()) + proxy->ignoreFilesWithPath().insert(root.relativeFilePath(resourceModel->indexDir().absolutePath())); ui->files->setModel(proxy); ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); diff --git a/launcher/ui/dialogs/ExportPackDialog.h b/launcher/ui/dialogs/ExportPackDialog.h index 830c24d25..ab20f44bd 100644 --- a/launcher/ui/dialogs/ExportPackDialog.h +++ b/launcher/ui/dialogs/ExportPackDialog.h @@ -18,6 +18,7 @@ #pragma once +#include #include #include "BaseInstance.h" #include "FastFileIconProvider.h" @@ -32,7 +33,7 @@ class ExportPackDialog : public QDialog { Q_OBJECT public: - explicit ExportPackDialog(InstancePtr instance, + explicit ExportPackDialog(const MinecraftInstancePtr& instance, QWidget* parent = nullptr, ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH); ~ExportPackDialog(); @@ -41,7 +42,7 @@ class ExportPackDialog : public QDialog { void validate(); private: - const InstancePtr instance; + const MinecraftInstancePtr instance; Ui::ExportPackDialog* ui; FileIgnoreProxy* proxy; FastFileIconProvider icons; From b66d6b2812acbea33359a0deb8d68ef12aaf4084 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Sat, 26 Oct 2024 13:24:03 +0100 Subject: [PATCH 20/39] Unhardcode PREFIXES Signed-off-by: TheKodeToad --- .../modplatform/flame/FlamePackExportTask.cpp | 40 +++++++++---------- .../modplatform/flame/FlamePackExportTask.h | 13 +++--- .../modrinth/ModrinthPackExportTask.cpp | 11 +++-- .../modrinth/ModrinthPackExportTask.h | 1 - 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index d661f1f05..61eaa826d 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "Application.h" #include "Json.h" #include "MMCZip.h" @@ -39,24 +40,23 @@ #include "tasks/Task.h" const QString FlamePackExportTask::TEMPLATE = "
  • {name}{authors}
  • \n"; -const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" }); +const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); -FlamePackExportTask::FlamePackExportTask(const QString& name, - const QString& version, - const QString& author, +FlamePackExportTask::FlamePackExportTask(QString name, + QString version, + QString author, bool optionalFiles, - InstancePtr instance, - const QString& output, + MinecraftInstancePtr instance, + QString output, MMCZip::FilterFunction filter) - : name(name) - , version(version) - , author(author) + : name(std::move(name)) + , version(std::move(version)) + , author(std::move(author)) , optionalFiles(optionalFiles) - , instance(instance) - , mcInstance(dynamic_cast(instance.get())) - , gameRoot(instance->gameRoot()) - , output(output) - , filter(filter) + , instance(std::move(instance)) + , gameRoot(this->instance->gameRoot()) + , output(std::move(output)) + , filter(std::move(filter)) {} void FlamePackExportTask::executeTask() @@ -90,11 +90,7 @@ void FlamePackExportTask::collectFiles() pendingHashes.clear(); resolvedFiles.clear(); - if (mcInstance != nullptr) { - mcInstance->loaderModList()->update(); - connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &FlamePackExportTask::collectHashes); - } else - collectHashes(); + collectHashes(); } void FlamePackExportTask::collectHashes() @@ -102,7 +98,7 @@ void FlamePackExportTask::collectHashes() setAbortable(true); setStatus(tr("Finding file hashes...")); setProgress(1, 5); - auto allMods = mcInstance->loaderModList()->allMods(); + auto allMods = instance->loaderModList()->allMods(); ConcurrentTask::Ptr hashingTask( new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); task.reset(hashingTask); @@ -379,9 +375,9 @@ QByteArray FlamePackExportTask::generateIndex() obj["version"] = version; obj["author"] = author; obj["overrides"] = "overrides"; - if (mcInstance) { + if (instance) { QJsonObject version; - auto profile = mcInstance->getPackProfile(); + auto profile = instance->getPackProfile(); // collect all supported components const ComponentPtr minecraft = profile->getComponent("net.minecraft"); const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index b11eb17fa..83d870b09 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -28,12 +28,12 @@ class FlamePackExportTask : public Task { Q_OBJECT public: - FlamePackExportTask(const QString& name, - const QString& version, - const QString& author, + FlamePackExportTask(QString name, + QString version, + QString author, bool optionalFiles, - InstancePtr instance, - const QString& output, + MinecraftInstancePtr instance, + QString output, MMCZip::FilterFunction filter); protected: @@ -47,8 +47,7 @@ class FlamePackExportTask : public Task { // inputs const QString name, version, author; const bool optionalFiles; - const InstancePtr instance; - MinecraftInstance* mcInstance; + const MinecraftInstancePtr instance; const QDir gameRoot; const QString output; const MMCZip::FilterFunction filter; diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index 8df6405ad..c1333963e 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -31,7 +31,6 @@ #include "modplatform/helpers/HashUtils.h" #include "tasks/Task.h" -const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); ModrinthPackExportTask::ModrinthPackExportTask(QString name, @@ -87,16 +86,22 @@ void ModrinthPackExportTask::collectFiles() void ModrinthPackExportTask::collectHashes() { + // TODO make this just use EnsureMetadataTask + setStatus(tr("Finding file hashes...")); + QStringList prefixes; + for (const auto& model : instance->resourceLists()) { - QCoreApplication::processEvents(); // TODO: maybe don't do this? + QCoreApplication::processEvents(); QEventLoop loop; connect(model.get(), &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); model->update(); loop.exec(); + prefixes.append(gameRoot.relativeFilePath(model->dir().absolutePath()) + '/'); + for (const Resource* resource : model->allResources()) { QCoreApplication::processEvents(); @@ -163,7 +168,7 @@ void ModrinthPackExportTask::collectHashes() continue; // require sensible file types - if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) + if (!std::any_of(prefixes.begin(), prefixes.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) continue; if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) { return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled"); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index 14bc36009..d5dc08678 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -48,7 +48,6 @@ class ModrinthPackExportTask : public Task { Metadata::ModSide side; }; - static const QStringList PREFIXES; static const QStringList FILE_EXTENSIONS; // inputs From 6a6fe60a5beddd29de48939d5726ca5671d3d01f Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 10:29:49 +0000 Subject: [PATCH 21/39] Revert "Use non-mod metadata in ModrinthPackExportTask" Out-of-scope Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 14 +- launcher/minecraft/MinecraftInstance.h | 1 - .../modplatform/flame/FlamePackExportTask.cpp | 40 ++-- .../modplatform/flame/FlamePackExportTask.h | 13 +- launcher/modplatform/helpers/HashUtils.cpp | 2 +- .../modrinth/ModrinthPackExportTask.cpp | 188 +++++++----------- .../modrinth/ModrinthPackExportTask.h | 14 +- launcher/ui/MainWindow.cpp | 27 +-- launcher/ui/dialogs/ExportPackDialog.cpp | 11 +- launcher/ui/dialogs/ExportPackDialog.h | 5 +- 10 files changed, 141 insertions(+), 174 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 87bd1b703..2b24ec090 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -522,8 +522,7 @@ QStringList MinecraftInstance::javaArguments() if (javaVersion.isModular() && shouldApplyOnlineFixes()) // allow reflective access to java.net - required by the skin fix - args << "--add-opens" - << "java.base/java.net=ALL-UNNAMED"; + args << "--add-opens" << "java.base/java.net=ALL-UNNAMED"; return args; } @@ -797,10 +796,8 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftT QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, MinecraftTarget::Ptr targetToJoin) { QStringList out; - out << "Main Class:" - << " " + getMainClass() << ""; - out << "Native path:" - << " " + getNativePath() << ""; + out << "Main Class:" << " " + getMainClass() << ""; + out << "Native path:" << " " + getNativePath() << ""; auto profile = m_components->getProfile(); @@ -1230,11 +1227,6 @@ std::shared_ptr MinecraftInstance::shaderPackList() return m_shader_pack_list; } -QList> MinecraftInstance::resourceLists() -{ - return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList() }; -} - std::shared_ptr MinecraftInstance::worldList() { if (!m_world_list) { diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 5d9bb45ef..75e97ae45 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -116,7 +116,6 @@ class MinecraftInstance : public BaseInstance { std::shared_ptr resourcePackList(); std::shared_ptr texturePackList(); std::shared_ptr shaderPackList(); - QList> resourceLists(); std::shared_ptr worldList(); std::shared_ptr gameOptionsModel(); diff --git a/launcher/modplatform/flame/FlamePackExportTask.cpp b/launcher/modplatform/flame/FlamePackExportTask.cpp index 61eaa826d..d661f1f05 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.cpp +++ b/launcher/modplatform/flame/FlamePackExportTask.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include "Application.h" #include "Json.h" #include "MMCZip.h" @@ -40,23 +39,24 @@ #include "tasks/Task.h" const QString FlamePackExportTask::TEMPLATE = "
  • {name}{authors}
  • \n"; -const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); +const QStringList FlamePackExportTask::FILE_EXTENSIONS({ "jar", "zip" }); -FlamePackExportTask::FlamePackExportTask(QString name, - QString version, - QString author, +FlamePackExportTask::FlamePackExportTask(const QString& name, + const QString& version, + const QString& author, bool optionalFiles, - MinecraftInstancePtr instance, - QString output, + InstancePtr instance, + const QString& output, MMCZip::FilterFunction filter) - : name(std::move(name)) - , version(std::move(version)) - , author(std::move(author)) + : name(name) + , version(version) + , author(author) , optionalFiles(optionalFiles) - , instance(std::move(instance)) - , gameRoot(this->instance->gameRoot()) - , output(std::move(output)) - , filter(std::move(filter)) + , instance(instance) + , mcInstance(dynamic_cast(instance.get())) + , gameRoot(instance->gameRoot()) + , output(output) + , filter(filter) {} void FlamePackExportTask::executeTask() @@ -90,7 +90,11 @@ void FlamePackExportTask::collectFiles() pendingHashes.clear(); resolvedFiles.clear(); - collectHashes(); + if (mcInstance != nullptr) { + mcInstance->loaderModList()->update(); + connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &FlamePackExportTask::collectHashes); + } else + collectHashes(); } void FlamePackExportTask::collectHashes() @@ -98,7 +102,7 @@ void FlamePackExportTask::collectHashes() setAbortable(true); setStatus(tr("Finding file hashes...")); setProgress(1, 5); - auto allMods = instance->loaderModList()->allMods(); + auto allMods = mcInstance->loaderModList()->allMods(); ConcurrentTask::Ptr hashingTask( new ConcurrentTask(this, "MakeHashesTask", APPLICATION->settings()->get("NumberOfConcurrentTasks").toInt())); task.reset(hashingTask); @@ -375,9 +379,9 @@ QByteArray FlamePackExportTask::generateIndex() obj["version"] = version; obj["author"] = author; obj["overrides"] = "overrides"; - if (instance) { + if (mcInstance) { QJsonObject version; - auto profile = instance->getPackProfile(); + auto profile = mcInstance->getPackProfile(); // collect all supported components const ComponentPtr minecraft = profile->getComponent("net.minecraft"); const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); diff --git a/launcher/modplatform/flame/FlamePackExportTask.h b/launcher/modplatform/flame/FlamePackExportTask.h index 83d870b09..b11eb17fa 100644 --- a/launcher/modplatform/flame/FlamePackExportTask.h +++ b/launcher/modplatform/flame/FlamePackExportTask.h @@ -28,12 +28,12 @@ class FlamePackExportTask : public Task { Q_OBJECT public: - FlamePackExportTask(QString name, - QString version, - QString author, + FlamePackExportTask(const QString& name, + const QString& version, + const QString& author, bool optionalFiles, - MinecraftInstancePtr instance, - QString output, + InstancePtr instance, + const QString& output, MMCZip::FilterFunction filter); protected: @@ -47,7 +47,8 @@ class FlamePackExportTask : public Task { // inputs const QString name, version, author; const bool optionalFiles; - const MinecraftInstancePtr instance; + const InstancePtr instance; + MinecraftInstance* mcInstance; const QDir gameRoot; const QString output; const MMCZip::FilterFunction filter; diff --git a/launcher/modplatform/helpers/HashUtils.cpp b/launcher/modplatform/helpers/HashUtils.cpp index 1db7ca453..a3b8d904c 100644 --- a/launcher/modplatform/helpers/HashUtils.cpp +++ b/launcher/modplatform/helpers/HashUtils.cpp @@ -115,7 +115,7 @@ QString hash(QIODevice* device, Algorithm type) QCryptographicHash hash(alg); if (!hash.addData(device)) - qCritical() << "Failed to read file to create hash!"; + qCritical() << "Failed to read JAR to create hash!"; Q_ASSERT(hash.result().length() == hash.hashLength(alg)); auto result = hash.result().toHex(); diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp index c1333963e..d103170af 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.cpp @@ -31,23 +31,25 @@ #include "modplatform/helpers/HashUtils.h" #include "tasks/Task.h" +const QStringList ModrinthPackExportTask::PREFIXES({ "mods/", "coremods/", "resourcepacks/", "texturepacks/", "shaderpacks/" }); const QStringList ModrinthPackExportTask::FILE_EXTENSIONS({ "jar", "litemod", "zip" }); -ModrinthPackExportTask::ModrinthPackExportTask(QString name, - QString version, - QString summary, +ModrinthPackExportTask::ModrinthPackExportTask(const QString& name, + const QString& version, + const QString& summary, bool optionalFiles, - MinecraftInstancePtr instance, - QString output, + InstancePtr instance, + const QString& output, MMCZip::FilterFunction filter) - : name(std::move(name)) - , version(std::move(version)) - , summary(std::move(summary)) + : name(name) + , version(version) + , summary(summary) , optionalFiles(optionalFiles) - , instance(std::move(instance)) - , gameRoot(this->instance->gameRoot()) - , output(std::move(output)) - , filter(std::move(filter)) + , instance(instance) + , mcInstance(dynamic_cast(instance.get())) + , gameRoot(instance->gameRoot()) + , output(output) + , filter(filter) {} void ModrinthPackExportTask::executeTask() @@ -81,102 +83,64 @@ void ModrinthPackExportTask::collectFiles() pendingHashes.clear(); resolvedFiles.clear(); - collectHashes(); + if (mcInstance) { + mcInstance->loaderModList()->update(); + connect(mcInstance->loaderModList().get(), &ModFolderModel::updateFinished, this, &ModrinthPackExportTask::collectHashes); + } else + collectHashes(); } void ModrinthPackExportTask::collectHashes() { - // TODO make this just use EnsureMetadataTask - setStatus(tr("Finding file hashes...")); - - QStringList prefixes; - - for (const auto& model : instance->resourceLists()) { - QCoreApplication::processEvents(); - - QEventLoop loop; - connect(model.get(), &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); - model->update(); - loop.exec(); - - prefixes.append(gameRoot.relativeFilePath(model->dir().absolutePath()) + '/'); - - for (const Resource* resource : model->allResources()) { - QCoreApplication::processEvents(); - - if (resource->metadata() == nullptr) - continue; - - const QUrl& url = resource->metadata()->url; - - if (url.isEmpty() || !BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) - continue; - - const QFileInfo& fileInfo = resource->fileinfo(); - const QString relativePath = gameRoot.relativeFilePath(fileInfo.absoluteFilePath()); - - if (filter(relativePath)) - continue; - - qDebug() << "Resolving" << relativePath << "from index"; - - QString sha1; - QString sha512; - qint64 size; - - if (resource->metadata()->hash_format == "sha1") - sha1 = resource->metadata()->hash; - else if (resource->metadata()->hash_format == "sha512") - sha512 = resource->metadata()->hash; - - { - QFile file(fileInfo.absoluteFilePath()); - - if (!file.open(QFile::ReadOnly)) { - qWarning() << "Could not open" << relativePath << "for hashing"; - continue; - } - - const QByteArray data = file.readAll(); - - if (file.error() != QFileDevice::NoError) { - qWarning() << "Could not read" << relativePath; - continue; - } - - if (sha1.isEmpty()) - sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1); - - if (sha512.isEmpty()) - sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512); - - size = file.size(); - } - - ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), size, resource->metadata()->side }; - resolvedFiles[relativePath] = resolvedFile; - } - } - for (const QFileInfo& file : files) { QCoreApplication::processEvents(); const QString relative = gameRoot.relativeFilePath(file.absoluteFilePath()); - - if (resolvedFiles.contains(relative)) - continue; - // require sensible file types - if (!std::any_of(prefixes.begin(), prefixes.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) + if (!std::any_of(PREFIXES.begin(), PREFIXES.end(), [&relative](const QString& prefix) { return relative.startsWith(prefix); })) continue; if (!std::any_of(FILE_EXTENSIONS.begin(), FILE_EXTENSIONS.end(), [&relative](const QString& extension) { return relative.endsWith('.' + extension) || relative.endsWith('.' + extension + ".disabled"); })) continue; + QFile openFile(file.absoluteFilePath()); + if (!openFile.open(QFile::ReadOnly)) { + qWarning() << "Could not open" << file << "for hashing"; + continue; + } + + const QByteArray data = openFile.readAll(); + if (openFile.error() != QFileDevice::NoError) { + qWarning() << "Could not read" << file; + continue; + } + auto sha512 = Hashing::hash(data, Hashing::Algorithm::Sha512); + + auto allMods = mcInstance->loaderModList()->allMods(); + if (auto modIter = std::find_if(allMods.begin(), allMods.end(), [&file](Mod* mod) { return mod->fileinfo() == file; }); + modIter != allMods.end()) { + const Mod* mod = *modIter; + if (mod->metadata() != nullptr) { + const QUrl& url = mod->metadata()->url; + // ensure the url is permitted on modrinth.com + if (!url.isEmpty() && BuildConfig.MODRINTH_MRPACK_HOSTS.contains(url.host())) { + qDebug() << "Resolving" << relative << "from index"; + + auto sha1 = Hashing::hash(data, Hashing::Algorithm::Sha1); + + ResolvedFile resolvedFile{ sha1, sha512, url.toEncoded(), openFile.size(), mod->metadata()->side }; + resolvedFiles[relative] = resolvedFile; + + // nice! we've managed to resolve based on local metadata! + // no need to enqueue it + continue; + } + } + } + qDebug() << "Enqueueing" << relative << "for Modrinth query"; - auto sha512 = Hashing::hash(file.absoluteFilePath(), Hashing::Algorithm::Sha512); pendingHashes[relative] = sha512; } @@ -277,28 +241,30 @@ QByteArray ModrinthPackExportTask::generateIndex() if (!summary.isEmpty()) out["summary"] = summary; - auto profile = instance->getPackProfile(); - // collect all supported components - const ComponentPtr minecraft = profile->getComponent("net.minecraft"); - const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); - const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); - const ComponentPtr forge = profile->getComponent("net.minecraftforge"); - const ComponentPtr neoForge = profile->getComponent("net.neoforged"); + if (mcInstance) { + auto profile = mcInstance->getPackProfile(); + // collect all supported components + const ComponentPtr minecraft = profile->getComponent("net.minecraft"); + const ComponentPtr quilt = profile->getComponent("org.quiltmc.quilt-loader"); + const ComponentPtr fabric = profile->getComponent("net.fabricmc.fabric-loader"); + const ComponentPtr forge = profile->getComponent("net.minecraftforge"); + const ComponentPtr neoForge = profile->getComponent("net.neoforged"); - // convert all available components to mrpack dependencies - QJsonObject dependencies; - if (minecraft != nullptr) - dependencies["minecraft"] = minecraft->m_version; - if (quilt != nullptr) - dependencies["quilt-loader"] = quilt->m_version; - if (fabric != nullptr) - dependencies["fabric-loader"] = fabric->m_version; - if (forge != nullptr) - dependencies["forge"] = forge->m_version; - if (neoForge != nullptr) - dependencies["neoforge"] = neoForge->m_version; + // convert all available components to mrpack dependencies + QJsonObject dependencies; + if (minecraft != nullptr) + dependencies["minecraft"] = minecraft->m_version; + if (quilt != nullptr) + dependencies["quilt-loader"] = quilt->m_version; + if (fabric != nullptr) + dependencies["fabric-loader"] = fabric->m_version; + if (forge != nullptr) + dependencies["forge"] = forge->m_version; + if (neoForge != nullptr) + dependencies["neoforge"] = neoForge->m_version; - out["dependencies"] = dependencies; + out["dependencies"] = dependencies; + } QJsonArray filesOut; for (auto iterator = resolvedFiles.constBegin(); iterator != resolvedFiles.constEnd(); iterator++) { diff --git a/launcher/modplatform/modrinth/ModrinthPackExportTask.h b/launcher/modplatform/modrinth/ModrinthPackExportTask.h index d5dc08678..ee740a456 100644 --- a/launcher/modplatform/modrinth/ModrinthPackExportTask.h +++ b/launcher/modplatform/modrinth/ModrinthPackExportTask.h @@ -29,12 +29,12 @@ class ModrinthPackExportTask : public Task { Q_OBJECT public: - ModrinthPackExportTask(QString name, - QString version, - QString summary, + ModrinthPackExportTask(const QString& name, + const QString& version, + const QString& summary, bool optionalFiles, - MinecraftInstancePtr instance, - QString output, + InstancePtr instance, + const QString& output, MMCZip::FilterFunction filter); protected: @@ -48,12 +48,14 @@ class ModrinthPackExportTask : public Task { Metadata::ModSide side; }; + static const QStringList PREFIXES; static const QStringList FILE_EXTENSIONS; // inputs const QString name, version, summary; const bool optionalFiles; - const MinecraftInstancePtr instance; + const InstancePtr instance; + MinecraftInstance* mcInstance; const QDir gameRoot; const QString output; const MMCZip::FilterFunction filter; diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 6134d9d31..96f1348d2 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1403,26 +1403,27 @@ void MainWindow::on_actionExportInstanceZip_triggered() void MainWindow::on_actionExportInstanceMrPack_triggered() { - auto instance = std::dynamic_pointer_cast(m_selectedInstance); - if (instance) { - ExportPackDialog dlg(std::move(instance), this); + if (m_selectedInstance) { + ExportPackDialog dlg(m_selectedInstance, this); dlg.exec(); } } void MainWindow::on_actionExportInstanceFlamePack_triggered() { - auto instance = std::dynamic_pointer_cast(m_selectedInstance); - if (instance) { - if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); - cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { - QMessageBox msgBox(this); - msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); - msgBox.exec(); - return; + if (m_selectedInstance) { + auto instance = dynamic_cast(m_selectedInstance.get()); + if (instance) { + if (auto cmp = instance->getPackProfile()->getComponent("net.minecraft"); + cmp && cmp->getVersionFile() && cmp->getVersionFile()->type == "snapshot") { + QMessageBox msgBox(this); + msgBox.setText("Snapshots are currently not supported by CurseForge modpacks."); + msgBox.exec(); + return; + } + ExportPackDialog dlg(m_selectedInstance, this, ModPlatform::ResourceProvider::FLAME); + dlg.exec(); } - ExportPackDialog dlg(std::move(instance), this, ModPlatform::ResourceProvider::FLAME); - dlg.exec(); } } diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index c01b83f5c..0278c6cb0 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -34,7 +34,7 @@ #include "MMCZip.h" #include "modplatform/modrinth/ModrinthPackExportTask.h" -ExportPackDialog::ExportPackDialog(const MinecraftInstancePtr& instance, QWidget* parent, ModPlatform::ResourceProvider provider) +ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPlatform::ResourceProvider provider) : QDialog(parent), instance(instance), ui(new Ui::ExportPackDialog), m_provider(provider) { Q_ASSERT(m_provider == ModPlatform::ResourceProvider::MODRINTH || m_provider == ModPlatform::ResourceProvider::FLAME); @@ -86,9 +86,12 @@ ExportPackDialog::ExportPackDialog(const MinecraftInstancePtr& instance, QWidget proxy->blockedPaths().insert(file); } - for (auto& resourceModel : instance->resourceLists()) - if (resourceModel->indexDir().exists()) - proxy->ignoreFilesWithPath().insert(root.relativeFilePath(resourceModel->indexDir().absolutePath())); + MinecraftInstance* mcInstance = dynamic_cast(instance.get()); + if (mcInstance) { + const QDir index = mcInstance->loaderModList()->indexDir(); + if (index.exists()) + proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath())); + } ui->files->setModel(proxy); ui->files->setRootIndex(proxy->mapFromSource(model->index(instance->gameRoot()))); diff --git a/launcher/ui/dialogs/ExportPackDialog.h b/launcher/ui/dialogs/ExportPackDialog.h index ab20f44bd..830c24d25 100644 --- a/launcher/ui/dialogs/ExportPackDialog.h +++ b/launcher/ui/dialogs/ExportPackDialog.h @@ -18,7 +18,6 @@ #pragma once -#include #include #include "BaseInstance.h" #include "FastFileIconProvider.h" @@ -33,7 +32,7 @@ class ExportPackDialog : public QDialog { Q_OBJECT public: - explicit ExportPackDialog(const MinecraftInstancePtr& instance, + explicit ExportPackDialog(InstancePtr instance, QWidget* parent = nullptr, ModPlatform::ResourceProvider provider = ModPlatform::ResourceProvider::MODRINTH); ~ExportPackDialog(); @@ -42,7 +41,7 @@ class ExportPackDialog : public QDialog { void validate(); private: - const MinecraftInstancePtr instance; + const InstancePtr instance; Ui::ExportPackDialog* ui; FileIgnoreProxy* proxy; FastFileIconProvider icons; From 1809858fc7d550713730750e238d9cfc57e0e0f5 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 11:01:30 +0000 Subject: [PATCH 22/39] Ignore .index in ExportPackDialog Signed-off-by: TheKodeToad --- launcher/minecraft/MinecraftInstance.cpp | 5 +++++ launcher/minecraft/MinecraftInstance.h | 1 + launcher/ui/dialogs/ExportPackDialog.cpp | 6 +++--- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 2b24ec090..0888714c8 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -1227,6 +1227,11 @@ std::shared_ptr MinecraftInstance::shaderPackList() return m_shader_pack_list; } +QList> MinecraftInstance::resourceLists() +{ + return { loaderModList(), coreModList(), nilModList(), resourcePackList(), texturePackList(), shaderPackList() }; +} + std::shared_ptr MinecraftInstance::worldList() { if (!m_world_list) { diff --git a/launcher/minecraft/MinecraftInstance.h b/launcher/minecraft/MinecraftInstance.h index 75e97ae45..5d9bb45ef 100644 --- a/launcher/minecraft/MinecraftInstance.h +++ b/launcher/minecraft/MinecraftInstance.h @@ -116,6 +116,7 @@ class MinecraftInstance : public BaseInstance { std::shared_ptr resourcePackList(); std::shared_ptr texturePackList(); std::shared_ptr shaderPackList(); + QList> resourceLists(); std::shared_ptr worldList(); std::shared_ptr gameOptionsModel(); diff --git a/launcher/ui/dialogs/ExportPackDialog.cpp b/launcher/ui/dialogs/ExportPackDialog.cpp index 0278c6cb0..8368d7bb7 100644 --- a/launcher/ui/dialogs/ExportPackDialog.cpp +++ b/launcher/ui/dialogs/ExportPackDialog.cpp @@ -88,9 +88,9 @@ ExportPackDialog::ExportPackDialog(InstancePtr instance, QWidget* parent, ModPla MinecraftInstance* mcInstance = dynamic_cast(instance.get()); if (mcInstance) { - const QDir index = mcInstance->loaderModList()->indexDir(); - if (index.exists()) - proxy->ignoreFilesWithPath().insert(root.relativeFilePath(index.absolutePath())); + for (auto& resourceModel : mcInstance->resourceLists()) + if (resourceModel->indexDir().exists()) + proxy->ignoreFilesWithPath().insert(root.relativeFilePath(resourceModel->indexDir().absolutePath())); } ui->files->setModel(proxy); From f57ca1e79e277cdfd91e3b71d13b5ec97c1870ea Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 16:07:41 +0000 Subject: [PATCH 23/39] Add Change Version action to all pages Signed-off-by: TheKodeToad --- .../ui/dialogs/ResourceDownloadDialog.cpp | 2 +- launcher/ui/dialogs/ResourceDownloadDialog.h | 4 +- .../pages/instance/ExternalResourcesPage.cpp | 4 ++ .../pages/instance/ExternalResourcesPage.ui | 13 +++-- launcher/ui/pages/instance/ModFolderPage.cpp | 2 +- .../ui/pages/instance/ResourcePackPage.cpp | 57 +++++++++++++++++++ launcher/ui/pages/instance/ResourcePackPage.h | 1 + launcher/ui/pages/instance/ShaderPackPage.cpp | 57 +++++++++++++++++++ launcher/ui/pages/instance/ShaderPackPage.h | 1 + .../ui/pages/instance/TexturePackPage.cpp | 57 +++++++++++++++++++ launcher/ui/pages/instance/TexturePackPage.h | 1 + 11 files changed, 190 insertions(+), 9 deletions(-) diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index 02a8454b4..f9b6716af 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -377,7 +377,7 @@ QList ShaderPackDownloadDialog::getPages() return pages; } -void ModDownloadDialog::setModMetadata(std::shared_ptr meta) +void ResourceDownloadDialog::setResourceMetadata(const std::shared_ptr& meta) { switch (meta->provider) { case ModPlatform::ResourceProvider::MODRINTH: diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.h b/launcher/ui/dialogs/ResourceDownloadDialog.h index 7a0d6e895..0c3e314bc 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.h +++ b/launcher/ui/dialogs/ResourceDownloadDialog.h @@ -69,6 +69,8 @@ class ResourceDownloadDialog : public QDialog, public BasePageProvider { const QList getTasks(); [[nodiscard]] const std::shared_ptr getBaseModel() const { return m_base_model; } + void setResourceMetadata(const std::shared_ptr& meta); + public slots: void accept() override; void reject() override; @@ -107,8 +109,6 @@ class ModDownloadDialog final : public ResourceDownloadDialog { QList getPages() override; GetModDependenciesTask::Ptr getModDependenciesTask() override; - void setModMetadata(std::shared_ptr); - private: BaseInstance* m_instance; }; diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 62bd6ac04..132297849 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -316,6 +316,10 @@ void ExternalResourcesPage::updateActions() const bool hasSelection = ui->treeView->selectionModel()->hasSelection(); ui->actionUpdateItem->setEnabled(!m_model->empty()); ui->actionResetItemMetadata->setEnabled(hasSelection); + + const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + ui->actionChangeVersion->setEnabled(rows.count() == 1 && m_model->at(m_filterModel->mapToSource(rows[0]).row()).metadata() != nullptr); + ui->actionRemoveItem->setEnabled(hasSelection); ui->actionEnableItem->setEnabled(hasSelection); ui->actionDisableItem->setEnabled(hasSelection); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index c33df2c26..b7b219717 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -60,7 +60,7 @@ true
    - QAbstractItemView::DragDropMode::DropOnly + QAbstractItemView::NoDragDrop true @@ -74,7 +74,7 @@ Actions - Qt::ToolButtonStyle::ToolButtonTextOnly + Qt::ToolButtonIconOnly true @@ -177,7 +177,7 @@ Reset Update Metadata - QAction::MenuRole::NoRole + QAction::NoRole
    @@ -185,7 +185,7 @@ Verify Dependencies - QAction::MenuRole::NoRole + QAction::NoRole @@ -200,6 +200,9 @@ + + false + Change Version @@ -207,7 +210,7 @@ Change a resource's version. - QAction::MenuRole::NoRole + QAction::NoRole diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 1ede31d49..40bf01f9a 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -308,7 +308,7 @@ void ModFolderPage::changeModVersion() return; ResourceDownload::ModDownloadDialog mdownload(this, m_model, m_instance); - mdownload.setModMetadata((*mods_list.begin())->metadata()); + mdownload.setResourceMetadata((*mods_list.begin())->metadata()); if (mdownload.exec()) { auto tasks = new ConcurrentTask(this, "Download Mods", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); connect(tasks, &Task::failed, [this, tasks](QString reason) { diff --git a/launcher/ui/pages/instance/ResourcePackPage.cpp b/launcher/ui/pages/instance/ResourcePackPage.cpp index d03511ca4..97d61058e 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.cpp +++ b/launcher/ui/pages/instance/ResourcePackPage.cpp @@ -67,6 +67,10 @@ ResourcePackPage::ResourcePackPage(MinecraftInstance* instance, std::shared_ptr< connect(ui->actionResetItemMetadata, &QAction::triggered, this, &ResourcePackPage::deleteResourcePackMetadata); ui->actionUpdateItem->setMenu(updateMenu); + + ui->actionChangeVersion->setToolTip(tr("Change a mod's version.")); + connect(ui->actionChangeVersion, &QAction::triggered, this, &ResourcePackPage::changeResourcePackVersion); + ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); } void ResourcePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -214,3 +218,56 @@ void ResourcePackPage::deleteResourcePackMetadata() m_model->deleteMetadata(selection); } + +void ResourcePackPage::changeResourcePackVersion() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Resource pack updates are unavailable when metadata is disabled!")); + return; + } + + const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + + if (rows.count() != 1) + return; + + Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row()); + + if (resource.metadata() == nullptr) + return; + + ResourceDownload::ResourcePackDownloadDialog mdownload(this, m_model, m_instance); + mdownload.setResourceMetadata(resource.metadata()); + if (mdownload.exec()) { + auto tasks = + new ConcurrentTask(this, "Download Resource Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + + tasks->deleteLater(); + }); + + for (auto& task : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} \ No newline at end of file diff --git a/launcher/ui/pages/instance/ResourcePackPage.h b/launcher/ui/pages/instance/ResourcePackPage.h index e95809cf8..55abe007c 100644 --- a/launcher/ui/pages/instance/ResourcePackPage.h +++ b/launcher/ui/pages/instance/ResourcePackPage.h @@ -64,6 +64,7 @@ class ResourcePackPage : public ExternalResourcesPage { void downloadResourcePacks(); void updateResourcePacks(); void deleteResourcePackMetadata(); + void changeResourcePackVersion(); protected: std::shared_ptr m_model; diff --git a/launcher/ui/pages/instance/ShaderPackPage.cpp b/launcher/ui/pages/instance/ShaderPackPage.cpp index 768d7c710..c734f539e 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.cpp +++ b/launcher/ui/pages/instance/ShaderPackPage.cpp @@ -70,6 +70,10 @@ ShaderPackPage::ShaderPackPage(MinecraftInstance* instance, std::shared_ptractionResetItemMetadata, &QAction::triggered, this, &ShaderPackPage::deleteShaderPackMetadata); ui->actionUpdateItem->setMenu(updateMenu); + + ui->actionChangeVersion->setToolTip(tr("Change a shader pack's version.")); + connect(ui->actionChangeVersion, &QAction::triggered, this, &ShaderPackPage::changeShaderPackVersion); + ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); } void ShaderPackPage::downloadShaderPack() @@ -207,3 +211,56 @@ void ShaderPackPage::deleteShaderPackMetadata() m_model->deleteMetadata(selection); } + +void ShaderPackPage::changeShaderPackVersion() +{ + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Shader pack updates are unavailable when metadata is disabled!")); + return; + } + + const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + + if (rows.count() != 1) + return; + + Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row()); + + if (resource.metadata() == nullptr) + return; + + ResourceDownload::ShaderPackDownloadDialog mdownload(this, m_model, m_instance); + mdownload.setResourceMetadata(resource.metadata()); + if (mdownload.exec()) { + auto tasks = + new ConcurrentTask(this, "Download Shader Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + + tasks->deleteLater(); + }); + + for (auto& task : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/ShaderPackPage.h b/launcher/ui/pages/instance/ShaderPackPage.h index f35a4b79f..ebf7f1d58 100644 --- a/launcher/ui/pages/instance/ShaderPackPage.h +++ b/launcher/ui/pages/instance/ShaderPackPage.h @@ -56,6 +56,7 @@ class ShaderPackPage : public ExternalResourcesPage { void downloadShaderPack(); void updateShaderPacks(); void deleteShaderPackMetadata(); + void changeShaderPackVersion(); private: std::shared_ptr m_model; diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index cdd2b0964..648cd2ec7 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -69,6 +69,11 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptractionResetItemMetadata, &QAction::triggered, this, &TexturePackPage::deleteTexturePackMetadata); ui->actionUpdateItem->setMenu(updateMenu); + + ui->actionChangeVersion->setToolTip(tr("Change a texture pack's version.")); + connect(ui->actionChangeVersion, &QAction::triggered, this, &TexturePackPage::changeTexturePackVersion); + ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); + } void TexturePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) @@ -216,3 +221,55 @@ void TexturePackPage::deleteTexturePackMetadata() m_model->deleteMetadata(selection); } + +void TexturePackPage::changeTexturePackVersion() { + if (m_instance->typeName() != "Minecraft") + return; // this is a null instance or a legacy instance + + if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { + QMessageBox::critical(this, tr("Error"), tr("Texture pack updates are unavailable when metadata is disabled!")); + return; + } + + const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + + if (rows.count() != 1) + return; + + Resource &resource = m_model->at(m_filterModel->mapToSource(rows[0]).row()); + + if (resource.metadata() == nullptr) + return; + + ResourceDownload::TexturePackDownloadDialog mdownload(this, m_model, m_instance); + mdownload.setResourceMetadata(resource.metadata()); + if (mdownload.exec()) { + auto tasks = + new ConcurrentTask(this, "Download Texture Packs", APPLICATION->settings()->get("NumberOfConcurrentDownloads").toInt()); + connect(tasks, &Task::failed, [this, tasks](QString reason) { + CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::aborted, [this, tasks]() { + CustomMessageBox::selectable(this, tr("Aborted"), tr("Download stopped by user."), QMessageBox::Information)->show(); + tasks->deleteLater(); + }); + connect(tasks, &Task::succeeded, [this, tasks]() { + QStringList warnings = tasks->warnings(); + if (warnings.count()) + CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + + tasks->deleteLater(); + }); + + for (auto& task : mdownload.getTasks()) { + tasks->addTask(task); + } + + ProgressDialog loadDialog(this); + loadDialog.setSkipButton(true, tr("Abort")); + loadDialog.execWithTask(tasks); + + m_model->update(); + } +} diff --git a/launcher/ui/pages/instance/TexturePackPage.h b/launcher/ui/pages/instance/TexturePackPage.h index 4dc2c16c2..28d7ba209 100644 --- a/launcher/ui/pages/instance/TexturePackPage.h +++ b/launcher/ui/pages/instance/TexturePackPage.h @@ -59,6 +59,7 @@ class TexturePackPage : public ExternalResourcesPage { void downloadTexturePacks(); void updateTexturePacks(); void deleteTexturePackMetadata(); + void changeTexturePackVersion(); private: std::shared_ptr m_model; From 49044e23de491e09119d620c6cad02a9d0f4f8e7 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 16:21:00 +0000 Subject: [PATCH 24/39] Fix Export List placement Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 2 ++ launcher/ui/pages/instance/ExternalResourcesPage.ui | 2 +- launcher/ui/pages/instance/ModFolderPage.cpp | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 132297849..c455045b5 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -323,6 +323,8 @@ void ExternalResourcesPage::updateActions() ui->actionRemoveItem->setEnabled(hasSelection); ui->actionEnableItem->setEnabled(hasSelection); ui->actionDisableItem->setEnabled(hasSelection); + + ui->actionExportMetadata->setEnabled(!m_model->empty()); } void ExternalResourcesPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index b7b219717..22ab556b9 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -190,7 +190,7 @@ - true + false Export List diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 40bf01f9a..17bbb3bbc 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -107,11 +107,11 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr connect(ui->actionChangeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion); ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); - ui->actionsToolbar->addSeparator(); - ui->actionExportMetadata->setToolTip(tr("Export mod's metadata to text.")); connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata); - ui->actionsToolbar->addAction(ui->actionExportMetadata); + ui->actionsToolbar->insertActionAfter(ui->actionDisableItem, ui->actionExportMetadata); + + ui->actionsToolbar->insertSeparator(ui->actionExportMetadata); } bool ModFolderPage::shouldDisplay() const From 354a0ed46e02c72fcef0ad5703fc5b31c8382917 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 18:01:01 +0000 Subject: [PATCH 25/39] Fix column sizes Signed-off-by: TheKodeToad --- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- launcher/minecraft/mod/ResourcePackFolderModel.cpp | 2 +- launcher/minecraft/mod/TexturePackFolderModel.cpp | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index a92648de9..3fd366385 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -256,7 +256,7 @@ class ResourceFolderModel : public QAbstractListModel { QList m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; QStringList m_column_names = { "Enable", "Name", "Last Modified", "Provider", "Size" }; QStringList m_column_names_translated = { tr("Enable"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") }; - QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, + QList m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; QList m_columnsHideable = { false, false, true, true, true }; QList m_columnsHiddenByDefault = { false, false, false, false, true }; diff --git a/launcher/minecraft/mod/ResourcePackFolderModel.cpp b/launcher/minecraft/mod/ResourcePackFolderModel.cpp index 84c34582b..9850bd22d 100644 --- a/launcher/minecraft/mod/ResourcePackFolderModel.cpp +++ b/launcher/minecraft/mod/ResourcePackFolderModel.cpp @@ -55,7 +55,7 @@ ResourcePackFolderModel::ResourcePackFolderModel(const QDir& dir, BaseInstance* QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Pack Format"), tr("Last Modified"), tr("Provider"), tr("Size") }); m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::PACK_FORMAT, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; - m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true, true }; } diff --git a/launcher/minecraft/mod/TexturePackFolderModel.cpp b/launcher/minecraft/mod/TexturePackFolderModel.cpp index 293567cc8..073ea7ca7 100644 --- a/launcher/minecraft/mod/TexturePackFolderModel.cpp +++ b/launcher/minecraft/mod/TexturePackFolderModel.cpp @@ -47,8 +47,8 @@ TexturePackFolderModel::TexturePackFolderModel(const QDir& dir, BaseInstance* in { m_column_names = QStringList({ "Enable", "Image", "Name", "Last Modified", "Provider", "Size" }); m_column_names_translated = QStringList({ tr("Enable"), tr("Image"), tr("Name"), tr("Last Modified"), tr("Provider"), tr("Size") }); - m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::SIZE }; - m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive }; + m_column_sort_keys = { SortType::ENABLED, SortType::NAME, SortType::NAME, SortType::DATE, SortType::PROVIDER, SortType::SIZE }; + m_column_resize_modes = { QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Stretch, QHeaderView::Interactive, QHeaderView::Interactive, QHeaderView::Interactive }; m_columnsHideable = { false, true, false, true, true, true }; m_columnsHiddenByDefault = { false, false, false, false, false, true }; } From 2b22a1aebea12d95456791fce88695bcb856cec2 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 20:14:56 +0000 Subject: [PATCH 26/39] Reimplement View Homepage I removed it for some reason, but now it's back for all pages! Signed-off-by: TheKodeToad --- launcher/minecraft/mod/Mod.cpp | 14 ++++------ launcher/minecraft/mod/Mod.h | 3 +- launcher/minecraft/mod/Resource.cpp | 8 ++++++ launcher/minecraft/mod/Resource.h | 1 + .../modplatform/helpers/ExportToModList.cpp | 12 ++++---- .../pages/instance/ExternalResourcesPage.cpp | 26 ++++++++++++++++- .../ui/pages/instance/ExternalResourcesPage.h | 2 ++ .../pages/instance/ExternalResourcesPage.ui | 13 +++++++++ launcher/ui/pages/instance/ModFolderPage.cpp | 6 ++-- .../ui/pages/instance/TexturePackPage.cpp | 1 + launcher/ui/widgets/InfoFrame.cpp | 28 ++++++++++++++++--- 11 files changed, 90 insertions(+), 24 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index c5b13ddd3..8d998f586 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -141,16 +141,14 @@ auto Mod::version() const -> QString return details().version; } -auto Mod::homeurl() const -> QString +auto Mod::homepage() const -> QString { - return details().homeurl; -} + QString metaUrl = Resource::homepage(); -auto Mod::metaurl() const -> QString -{ - if (metadata() == nullptr) - return homeurl(); - return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id); + if (metaUrl.isEmpty()) + return details().homeurl; + else + return metaUrl; } auto Mod::loaders() const -> QString diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index a86a2fad0..228686bec 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -61,12 +61,11 @@ class Mod : public Resource { auto details() const -> const ModDetails&; auto name() const -> QString override; auto version() const -> QString; - auto homeurl() const -> QString; + auto homepage() const -> QString override; auto description() const -> QString; auto authors() const -> QStringList; auto licenses() const -> const QList&; auto issueTracker() const -> QString; - auto metaurl() const -> QString; auto side() const -> QString; auto loaders() const -> QString; auto mcVersions() const -> QString; diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index c7ef145b6..d1a7b8f9c 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -95,6 +95,14 @@ auto Resource::provider() const -> QString return tr("Unknown"); } +auto Resource::homepage() const -> QString +{ + if (metadata()) + return ModPlatform::getMetaURL(metadata()->provider, metadata()->project_id); + + return {}; +} + void Resource::setMetadata(std::shared_ptr&& metadata) { if (status() == ResourceStatus::NO_METADATA) diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index f6667871a..269f65859 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -100,6 +100,7 @@ class Resource : public QObject { [[nodiscard]] auto metadata() -> std::shared_ptr { return m_metadata; } [[nodiscard]] auto metadata() const -> std::shared_ptr { return m_metadata; } [[nodiscard]] auto provider() const -> QString; + [[nodiscard]] virtual auto homepage() const -> QString; void setStatus(ResourceStatus status) { m_status = status; } void setMetadata(std::shared_ptr&& metadata); diff --git a/launcher/modplatform/helpers/ExportToModList.cpp b/launcher/modplatform/helpers/ExportToModList.cpp index aea16ab50..bddc7e320 100644 --- a/launcher/modplatform/helpers/ExportToModList.cpp +++ b/launcher/modplatform/helpers/ExportToModList.cpp @@ -28,7 +28,7 @@ QString toHTML(QList mods, OptionalData extraData) auto meta = mod->metadata(); auto modName = mod->name().toHtmlEscaped(); if (extraData & Url) { - auto url = mod->metaurl().toHtmlEscaped(); + auto url = mod->homepage().toHtmlEscaped(); if (!url.isEmpty()) modName = QString("%2").arg(url, modName); } @@ -65,7 +65,7 @@ QString toMarkdown(QList mods, OptionalData extraData) auto meta = mod->metadata(); auto modName = toMarkdownEscaped(mod->name()); if (extraData & Url) { - auto url = mod->metaurl(); + auto url = mod->homepage(); if (!url.isEmpty()) modName = QString("[%1](%2)").arg(modName, url); } @@ -95,7 +95,7 @@ QString toPlainTXT(QList mods, OptionalData extraData) auto line = modName; if (extraData & Url) { - auto url = mod->metaurl(); + auto url = mod->homepage(); if (!url.isEmpty()) line += QString(" (%1)").arg(url); } @@ -124,7 +124,7 @@ QString toJSON(QList mods, OptionalData extraData) QJsonObject line; line["name"] = modName; if (extraData & Url) { - auto url = mod->metaurl(); + auto url = mod->homepage(); if (!url.isEmpty()) line["url"] = url; } @@ -156,7 +156,7 @@ QString toCSV(QList mods, OptionalData extraData) data << modName; if (extraData & Url) - data << mod->metaurl(); + data << mod->homepage(); if (extraData & Version) { auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) @@ -203,7 +203,7 @@ QString exportToModList(QList mods, QString lineTemplate) for (auto mod : mods) { auto meta = mod->metadata(); auto modName = mod->name(); - auto url = mod->metaurl(); + auto url = mod->homepage(); auto ver = mod->version(); if (ver.isEmpty() && meta != nullptr) ver = meta->version().toString(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index c455045b5..0e6c5131b 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -74,6 +74,7 @@ ExternalResourcesPage::ExternalResourcesPage(BaseInstance* instance, std::shared connect(ui->actionRemoveItem, &QAction::triggered, this, &ExternalResourcesPage::removeItem); connect(ui->actionEnableItem, &QAction::triggered, this, &ExternalResourcesPage::enableItem); connect(ui->actionDisableItem, &QAction::triggered, this, &ExternalResourcesPage::disableItem); + connect(ui->actionViewHomepage, &QAction::triggered, this, &ExternalResourcesPage::viewHomepage); connect(ui->actionViewConfigs, &QAction::triggered, this, &ExternalResourcesPage::viewConfigs); connect(ui->actionViewFolder, &QAction::triggered, this, &ExternalResourcesPage::viewFolder); @@ -301,6 +302,27 @@ void ExternalResourcesPage::disableItem() m_model->setResourceEnabled(selection.indexes(), EnableAction::DISABLE); } +void ExternalResourcesPage::viewHomepage() +{ + auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + bool openedAny = false; + for (auto resource : m_model->selectedResources(selection)) { + auto url = resource->homepage(); + if (!url.isEmpty()) { + DesktopServices::openUrl(url); + openedAny = true; + } + } + + // TODO: just disable button + // just doing this for now to prevent race conditions which may be worse with implementation changes + if (!openedAny) { + CustomMessageBox::selectable(this, tr("No homepages found"), tr("None of the selected resources had an available homepage."), + QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) + ->exec(); + } +} + void ExternalResourcesPage::viewConfigs() { DesktopServices::openPath(m_instance->instanceConfigFolder(), true); @@ -314,16 +336,18 @@ void ExternalResourcesPage::viewFolder() void ExternalResourcesPage::updateActions() { const bool hasSelection = ui->treeView->selectionModel()->hasSelection(); + const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + ui->actionUpdateItem->setEnabled(!m_model->empty()); ui->actionResetItemMetadata->setEnabled(hasSelection); - const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); ui->actionChangeVersion->setEnabled(rows.count() == 1 && m_model->at(m_filterModel->mapToSource(rows[0]).row()).metadata() != nullptr); ui->actionRemoveItem->setEnabled(hasSelection); ui->actionEnableItem->setEnabled(hasSelection); ui->actionDisableItem->setEnabled(hasSelection); + ui->actionViewHomepage->setEnabled(hasSelection); ui->actionExportMetadata->setEnabled(!m_model->empty()); } diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.h b/launcher/ui/pages/instance/ExternalResourcesPage.h index 9bbd13984..00bb5d17d 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.h +++ b/launcher/ui/pages/instance/ExternalResourcesPage.h @@ -56,6 +56,8 @@ class ExternalResourcesPage : public QMainWindow, public BasePage { virtual void enableItem(); virtual void disableItem(); + virtual void viewHomepage(); + virtual void viewFolder(); virtual void viewConfigs(); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.ui b/launcher/ui/pages/instance/ExternalResourcesPage.ui index 22ab556b9..9c41f3a71 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.ui +++ b/launcher/ui/pages/instance/ExternalResourcesPage.ui @@ -90,6 +90,8 @@ + + @@ -213,6 +215,17 @@ QAction::NoRole + + + false + + + View Homepage + + + View the homepages of all selected items. + + diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 17bbb3bbc..b9e0cfeef 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -107,11 +107,11 @@ ModFolderPage::ModFolderPage(BaseInstance* inst, std::shared_ptr connect(ui->actionChangeVersion, &QAction::triggered, this, &ModFolderPage::changeModVersion); ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); + ui->actionViewHomepage->setToolTip(tr("View the homepages of all selected mods.")); + ui->actionExportMetadata->setToolTip(tr("Export mod's metadata to text.")); connect(ui->actionExportMetadata, &QAction::triggered, this, &ModFolderPage::exportModMetadata); - ui->actionsToolbar->insertActionAfter(ui->actionDisableItem, ui->actionExportMetadata); - - ui->actionsToolbar->insertSeparator(ui->actionExportMetadata); + ui->actionsToolbar->insertActionAfter(ui->actionViewHomepage, ui->actionExportMetadata); } bool ModFolderPage::shouldDisplay() const diff --git a/launcher/ui/pages/instance/TexturePackPage.cpp b/launcher/ui/pages/instance/TexturePackPage.cpp index 648cd2ec7..990ee2b2e 100644 --- a/launcher/ui/pages/instance/TexturePackPage.cpp +++ b/launcher/ui/pages/instance/TexturePackPage.cpp @@ -74,6 +74,7 @@ TexturePackPage::TexturePackPage(MinecraftInstance* instance, std::shared_ptractionChangeVersion, &QAction::triggered, this, &TexturePackPage::changeTexturePackVersion); ui->actionsToolbar->insertActionAfter(ui->actionUpdateItem, ui->actionChangeVersion); + ui->actionViewHomepage->setToolTip(tr("View the homepages of all selected texture packs.")); } void TexturePackPage::updateFrame(const QModelIndex& current, [[maybe_unused]] const QModelIndex& previous) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 44f702659..2ae5d939b 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -84,7 +84,7 @@ void InfoFrame::updateWithMod(Mod const& m) QString text = ""; QString name = ""; - QString link = m.metaurl(); + QString link = m.homepage(); if (m.name().isEmpty()) name = m.internal_id(); else @@ -93,7 +93,7 @@ void InfoFrame::updateWithMod(Mod const& m) if (link.isEmpty()) text = name; else { - text = "" + name + ""; + text = "" + name + ""; } if (!m.authors().isEmpty()) text += " by " + m.authors().join(", "); @@ -145,6 +145,12 @@ void InfoFrame::updateWithMod(Mod const& m) void InfoFrame::updateWithResource(const Resource& resource) { + const QString homepage = resource.homepage(); + + if (!homepage.isEmpty()) { + + } + setName(resource.name()); setImage(); } @@ -209,14 +215,28 @@ QString InfoFrame::renderColorCodes(QString input) void InfoFrame::updateWithResourcePack(ResourcePack& resource_pack) { - setName(renderColorCodes(resource_pack.name())); + QString name = renderColorCodes(resource_pack.name()); + + const QString homepage = resource_pack.homepage(); + if (!homepage.isEmpty()) { + name = "" + name + ""; + } + + setName(name); setDescription(renderColorCodes(resource_pack.description())); setImage(resource_pack.image({ 64, 64 })); } void InfoFrame::updateWithTexturePack(TexturePack& texture_pack) { - setName(renderColorCodes(texture_pack.name())); + QString name = renderColorCodes(texture_pack.name()); + + const QString homepage = texture_pack.homepage(); + if (!homepage.isEmpty()) { + name = "" + name + ""; + } + + setName(name); setDescription(renderColorCodes(texture_pack.description())); setImage(texture_pack.image({ 64, 64 })); } From ad1d5ddcdfc86328490c05359d58cfd68de72aad Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 20:42:59 +0000 Subject: [PATCH 27/39] Revert unwise refactor Signed-off-by: TheKodeToad --- .../minecraft/mod/ResourceFolderModel.cpp | 34 +++++++++---------- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 4c7af3c02..9bc9cd9a9 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -483,19 +483,19 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const switch (role) { case Qt::DisplayRole: switch (column) { - case NAME_COLUMN: + case NameColumn: return m_resources[row]->name(); - case DATE_COLUMN: + case DateColumn: return m_resources[row]->dateTimeChanged(); - case PROVIDER_COLUMN: + case ProviderColumn: return m_resources[row]->provider(); - case SIZE_COLUMN: + case SizeColumn: return m_resources[row]->sizeStr(); default: return {}; } case Qt::ToolTipRole: - if (column == NAME_COLUMN) { + if (column == NameColumn) { 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." @@ -511,14 +511,14 @@ QVariant ResourceFolderModel::data(const QModelIndex& index, int role) const return m_resources[row]->internal_id(); case Qt::DecorationRole: { - if (column == NAME_COLUMN && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) + if (column == NameColumn && (at(row).isSymLinkUnder(instDirPath()) || at(row).isMoreThanOneHardLink())) return APPLICATION->getThemedIcon("status-yellow"); return {}; } case Qt::CheckStateRole: switch (column) { - case ACTIVE_COLUMN: + case ActiveColumn: return m_resources[row]->enabled() ? Qt::Checked : Qt::Unchecked; default: return {}; @@ -557,11 +557,11 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien switch (role) { case Qt::DisplayRole: switch (section) { - case ACTIVE_COLUMN: - case NAME_COLUMN: - case DATE_COLUMN: - case PROVIDER_COLUMN: - case SIZE_COLUMN: + case ActiveColumn: + case NameColumn: + case DateColumn: + case ProviderColumn: + case SizeColumn: return columnNames().at(section); default: return {}; @@ -569,15 +569,15 @@ QVariant ResourceFolderModel::headerData(int section, [[maybe_unused]] Qt::Orien case Qt::ToolTipRole: { //: Here, resource is a generic term for external resources, like Mods, Resource Packs, Shader Packs, etc. switch (section) { - case ACTIVE_COLUMN: + case ActiveColumn: return tr("Is the resource enabled?"); - case NAME_COLUMN: + case NameColumn: return tr("The name of the resource."); - case DATE_COLUMN: + case DateColumn: return tr("The date and time this resource was last changed (or added)."); - case PROVIDER_COLUMN: + case ProviderColumn: return tr("The source provider of the resource."); - case SIZE_COLUMN: + case SizeColumn: return tr("The size of the resource."); default: return {}; diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 3fd366385..80698eac5 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -151,7 +151,7 @@ class ResourceFolderModel : public QAbstractListModel { /* Qt behavior */ /* Basic columns */ - enum Columns { ACTIVE_COLUMN = 0, NAME_COLUMN, DATE_COLUMN, PROVIDER_COLUMN, SIZE_COLUMN, NUM_COLUMNS }; + enum Columns { ActiveColumn = 0, NameColumn, DateColumn, ProviderColumn, SizeColumn, NUM_COLUMNS }; QStringList columnNames(bool translated = true) const { return translated ? m_column_names_translated : m_column_names; } From f3cae427f197c614cc05ad55084edc32051093bd Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 21:09:45 +0000 Subject: [PATCH 28/39] Fix tests (make them green) Signed-off-by: TheKodeToad --- launcher/minecraft/mod/ModFolderModel.h | 1 - tests/ResourceFolderModel_test.cpp | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 629a2621c..c9a5fc226 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -75,7 +75,6 @@ class ModFolderModel : public ResourceFolderModel { ReleaseTypeColumn, NUM_COLUMNS }; - enum ModStatusAction { Disable, Enable, Toggle }; ModFolderModel(const QDir& dir, BaseInstance* instance, bool is_indexed, bool create_dir, QObject* parent = nullptr); virtual QString id() const override { return "mods"; } diff --git a/tests/ResourceFolderModel_test.cpp b/tests/ResourceFolderModel_test.cpp index 350ab615e..f2201a5e9 100644 --- a/tests/ResourceFolderModel_test.cpp +++ b/tests/ResourceFolderModel_test.cpp @@ -87,7 +87,7 @@ class ResourceFolderModelTest : public QObject { QEventLoop loop; - ModFolderModel m(tempDir.path(), nullptr, true); + ModFolderModel m(tempDir.path(), nullptr, true, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); @@ -96,7 +96,7 @@ class ResourceFolderModelTest : public QObject { expire_timer.setSingleShot(true); expire_timer.start(4000); - m.installMod(folder); + m.installResource(folder); loop.exec(); @@ -111,7 +111,7 @@ class ResourceFolderModelTest : public QObject { QString folder = source + '/'; QTemporaryDir tempDir; QEventLoop loop; - ModFolderModel m(tempDir.path(), nullptr, true); + ModFolderModel m(tempDir.path(), nullptr, true, true); connect(&m, &ModFolderModel::updateFinished, &loop, &QEventLoop::quit); @@ -120,7 +120,7 @@ class ResourceFolderModelTest : public QObject { expire_timer.setSingleShot(true); expire_timer.start(4000); - m.installMod(folder); + m.installResource(folder); loop.exec(); @@ -134,7 +134,7 @@ class ResourceFolderModelTest : public QObject { void test_addFromWatch() { QString source = QFINDTESTDATA("testdata/ResourceFolderModel"); - ModFolderModel model(source, nullptr); + ModFolderModel model(source, nullptr, false, true); QCOMPARE(model.size(), 0); @@ -154,7 +154,7 @@ class ResourceFolderModelTest : public QObject { QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar"); QTemporaryDir tmp; - ResourceFolderModel model(QDir(tmp.path()), nullptr); + ResourceFolderModel model(QDir(tmp.path()), nullptr, false, false); QCOMPARE(model.size(), 0); @@ -199,7 +199,7 @@ class ResourceFolderModelTest : public QObject { QString file_mod = QFINDTESTDATA("testdata/ResourceFolderModel/supercoolmod.jar"); QTemporaryDir tmp; - ResourceFolderModel model(tmp.path(), nullptr); + ResourceFolderModel model(tmp.path(), nullptr, false, false); QCOMPARE(model.size(), 0); @@ -210,7 +210,7 @@ class ResourceFolderModelTest : public QObject { EXEC_UPDATE_TASK(model.installResource(file_mod), QVERIFY) } - for (auto res : model.all()) + for (auto res : model.allResources()) qDebug() << res->name(); QCOMPARE(model.size(), 2); From bd8b30c606dda9532e50477fb05bf7513c4837e6 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 22:17:16 +0000 Subject: [PATCH 29/39] Implement link for InfoFrame::updateWithResource Signed-off-by: TheKodeToad --- launcher/ui/widgets/InfoFrame.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/launcher/ui/widgets/InfoFrame.cpp b/launcher/ui/widgets/InfoFrame.cpp index 2ae5d939b..3ef5dcb88 100644 --- a/launcher/ui/widgets/InfoFrame.cpp +++ b/launcher/ui/widgets/InfoFrame.cpp @@ -147,11 +147,11 @@ void InfoFrame::updateWithResource(const Resource& resource) { const QString homepage = resource.homepage(); - if (!homepage.isEmpty()) { + if (!homepage.isEmpty()) + setName("" + resource.name() + ""); + else + setName(resource.name()); - } - - setName(resource.name()); setImage(); } From d157159106df4ec8108309b3e0928f1589274f34 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Mon, 28 Oct 2024 22:29:43 +0000 Subject: [PATCH 30/39] Fix license headers and formatting Signed-off-by: TheKodeToad --- launcher/CMakeLists.txt | 8 ++++---- launcher/minecraft/mod/MetadataHandler.h | 1 + launcher/minecraft/mod/Mod.cpp | 1 + launcher/minecraft/mod/Mod.h | 1 + launcher/minecraft/mod/ModFolderModel.cpp | 1 + 5 files changed, 8 insertions(+), 4 deletions(-) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index db3762a8b..486aaff3e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -349,8 +349,8 @@ set(MINECRAFT_SOURCES minecraft/mod/tasks/ResourceFolderLoadTask.cpp minecraft/mod/tasks/LocalModParseTask.h minecraft/mod/tasks/LocalModParseTask.cpp - minecraft/mod/tasks/LocalResourceUpdateTask.h - minecraft/mod/tasks/LocalResourceUpdateTask.cpp + minecraft/mod/tasks/LocalResourceUpdateTask.h + minecraft/mod/tasks/LocalResourceUpdateTask.cpp minecraft/mod/tasks/LocalDataPackParseTask.h minecraft/mod/tasks/LocalDataPackParseTask.cpp minecraft/mod/tasks/LocalResourcePackParseTask.h @@ -1068,8 +1068,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/BlockedModsDialog.h ui/dialogs/ChooseProviderDialog.h ui/dialogs/ChooseProviderDialog.cpp - ui/dialogs/ResourceUpdateDialog.cpp - ui/dialogs/ResourceUpdateDialog.h + ui/dialogs/ResourceUpdateDialog.cpp + ui/dialogs/ResourceUpdateDialog.h ui/dialogs/InstallLoaderDialog.cpp ui/dialogs/InstallLoaderDialog.h diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 653c3c1ee..0b8cb124d 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * 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 diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 8d998f586..4986d8ee1 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 228686bec..8a352c66c 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -2,6 +2,7 @@ /* * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln + * Copyright (c) 2023 Trial97 * * 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 diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 11c784b96..8fe68203c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -3,6 +3,7 @@ * Prism Launcher - Minecraft Launcher * Copyright (c) 2022 flowln * Copyright (C) 2022 Sefa Eyeoglu + * Copyright (c) 2023 Trial97 * * 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 From c1c198b84b65ebac576354381c56c45fe18320a7 Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Oct 2024 15:45:30 +0000 Subject: [PATCH 31/39] Combine if statements Signed-off-by: TheKodeToad --- launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index e38ca526f..ead8238ff 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -168,13 +168,12 @@ void ModrinthCheckUpdate::checkNextLoader() return; } - if (m_loaders_list.size() == 0) { - if (m_loader_idx == 0) { - getUpdateModsForLoader({}); - m_loader_idx++; - return; - } + if (m_loaders_list.isEmpty() && m_loader_idx == 0) { + getUpdateModsForLoader({}); + m_loader_idx++; + return; } + if (m_loader_idx < m_loaders_list.size()) { getUpdateModsForLoader(m_loaders_list.at(m_loader_idx)); m_loader_idx++; From d49e6187cdccfc35ae5a670a7c8445399146dfec Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Oct 2024 17:14:59 +0000 Subject: [PATCH 32/39] Make requested changes Signed-off-by: TheKodeToad --- .../minecraft/mod/ResourceFolderModel.cpp | 2 +- launcher/minecraft/mod/ResourceFolderModel.h | 2 +- launcher/modplatform/CheckUpdateTask.h | 2 +- launcher/modplatform/ModIndex.h | 1 - .../modrinth/ModrinthCheckUpdate.cpp | 4 ++-- launcher/ui/MainWindow.cpp | 2 +- .../pages/instance/ExternalResourcesPage.cpp | 21 ++++++------------- 7 files changed, 12 insertions(+), 22 deletions(-) diff --git a/launcher/minecraft/mod/ResourceFolderModel.cpp b/launcher/minecraft/mod/ResourceFolderModel.cpp index 9bc9cd9a9..d349b2c56 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.cpp +++ b/launcher/minecraft/mod/ResourceFolderModel.cpp @@ -166,7 +166,7 @@ bool ResourceFolderModel::installResource(QString original_path) return false; } -bool ResourceFolderModel::installResource(QString path, ModPlatform::IndexedVersion& vers) +bool ResourceFolderModel::installResourceWithFlameMetadata(QString path, ModPlatform::IndexedVersion& vers) { if (vers.addonId.isValid()) { ModPlatform::IndexedPack pack{ diff --git a/launcher/minecraft/mod/ResourceFolderModel.h b/launcher/minecraft/mod/ResourceFolderModel.h index 80698eac5..1da748e05 100644 --- a/launcher/minecraft/mod/ResourceFolderModel.h +++ b/launcher/minecraft/mod/ResourceFolderModel.h @@ -113,7 +113,7 @@ class ResourceFolderModel : public QAbstractListModel { */ virtual bool installResource(QString path); - virtual bool installResource(QString path, ModPlatform::IndexedVersion& vers); + virtual bool installResourceWithFlameMetadata(QString path, ModPlatform::IndexedVersion& vers); /** Uninstall (i.e. remove all data about it) a resource, given its file name. * diff --git a/launcher/modplatform/CheckUpdateTask.h b/launcher/modplatform/CheckUpdateTask.h index 9d0b1c51a..1ee820a63 100644 --- a/launcher/modplatform/CheckUpdateTask.h +++ b/launcher/modplatform/CheckUpdateTask.h @@ -17,7 +17,7 @@ class CheckUpdateTask : public Task { std::list& mcVersions, QList loadersList, std::shared_ptr resourceModel) - : Task(nullptr) + : Task() , m_resources(resources) , m_game_versions(mcVersions) , m_loaders_list(std::move(loadersList)) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 11a230798..d5ee12473 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -25,7 +25,6 @@ #include #include #include -#include class QIODevice; diff --git a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp index ead8238ff..52db95077 100644 --- a/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp +++ b/launcher/modplatform/modrinth/ModrinthCheckUpdate.cpp @@ -148,7 +148,7 @@ void ModrinthCheckUpdate::checkVersionsResponse(std::shared_ptr resp } 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); + project_ver.changelog, ModPlatform::ResourceProvider::MODRINTH, download_task, resource->enabled()); } m_deps.append(std::make_shared(pack, project_ver)); @@ -177,7 +177,7 @@ void ModrinthCheckUpdate::checkNextLoader() if (m_loader_idx < m_loaders_list.size()) { getUpdateModsForLoader(m_loaders_list.at(m_loader_idx)); m_loader_idx++; - return; + return; } for (auto resource : m_mappings) { diff --git a/launcher/ui/MainWindow.cpp b/launcher/ui/MainWindow.cpp index 96f1348d2..a0bc83171 100644 --- a/launcher/ui/MainWindow.cpp +++ b/launcher/ui/MainWindow.cpp @@ -1048,7 +1048,7 @@ void MainWindow::processURLs(QList urls) qWarning() << "Importing of Data Packs not supported at this time. Ignoring" << localFileName; break; case PackedResourceType::Mod: - minecraftInst->loaderModList()->installResource(localFileName, version); + minecraftInst->loaderModList()->installResourceWithFlameMetadata(localFileName, version); break; case PackedResourceType::ShaderPack: minecraftInst->shaderPackList()->installResource(localFileName); diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index 0e6c5131b..d85726eb8 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -305,21 +305,10 @@ void ExternalResourcesPage::disableItem() void ExternalResourcesPage::viewHomepage() { auto selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); - bool openedAny = false; for (auto resource : m_model->selectedResources(selection)) { auto url = resource->homepage(); - if (!url.isEmpty()) { + if (!url.isEmpty()) DesktopServices::openUrl(url); - openedAny = true; - } - } - - // TODO: just disable button - // just doing this for now to prevent race conditions which may be worse with implementation changes - if (!openedAny) { - CustomMessageBox::selectable(this, tr("No homepages found"), tr("None of the selected resources had an available homepage."), - QMessageBox::Warning, QMessageBox::Ok, QMessageBox::Ok) - ->exec(); } } @@ -336,18 +325,20 @@ void ExternalResourcesPage::viewFolder() void ExternalResourcesPage::updateActions() { const bool hasSelection = ui->treeView->selectionModel()->hasSelection(); - const QModelIndexList rows = ui->treeView->selectionModel()->selectedRows(); + const QModelIndexList selection = m_filterModel->mapSelectionToSource(ui->treeView->selectionModel()->selection()).indexes(); + const QList selectedResources = m_model->selectedResources(selection); ui->actionUpdateItem->setEnabled(!m_model->empty()); ui->actionResetItemMetadata->setEnabled(hasSelection); - ui->actionChangeVersion->setEnabled(rows.count() == 1 && m_model->at(m_filterModel->mapToSource(rows[0]).row()).metadata() != nullptr); + ui->actionChangeVersion->setEnabled(selectedResources.size() == 1 && selectedResources[0]->metadata() != nullptr); ui->actionRemoveItem->setEnabled(hasSelection); ui->actionEnableItem->setEnabled(hasSelection); ui->actionDisableItem->setEnabled(hasSelection); - ui->actionViewHomepage->setEnabled(hasSelection); + ui->actionViewHomepage->setEnabled(hasSelection && std::any_of(selectedResources.begin(), selectedResources.end(), + [](Resource* resource) { return !resource->homepage().isEmpty() })); ui->actionExportMetadata->setEnabled(!m_model->empty()); } From 2368521a9ae7cd1dbc2c3bae3edaa914924ebcff Mon Sep 17 00:00:00 2001 From: TheKodeToad Date: Tue, 29 Oct 2024 17:53:57 +0000 Subject: [PATCH 33/39] Add missing semicolon Signed-off-by: TheKodeToad --- launcher/ui/pages/instance/ExternalResourcesPage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/ExternalResourcesPage.cpp b/launcher/ui/pages/instance/ExternalResourcesPage.cpp index d85726eb8..4ea12cb6d 100644 --- a/launcher/ui/pages/instance/ExternalResourcesPage.cpp +++ b/launcher/ui/pages/instance/ExternalResourcesPage.cpp @@ -338,7 +338,7 @@ void ExternalResourcesPage::updateActions() ui->actionDisableItem->setEnabled(hasSelection); ui->actionViewHomepage->setEnabled(hasSelection && std::any_of(selectedResources.begin(), selectedResources.end(), - [](Resource* resource) { return !resource->homepage().isEmpty() })); + [](Resource* resource) { return !resource->homepage().isEmpty(); })); ui->actionExportMetadata->setEnabled(!m_model->empty()); } From fd109c47401c755e35da9c2884e9b38532bed56c Mon Sep 17 00:00:00 2001 From: Ben Westover Date: Tue, 29 Oct 2024 18:20:00 +0000 Subject: [PATCH 34/39] Shorten metainfo.xml app summary Signed-off-by: Ben Westover --- program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in b/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in index a482f0e38..3c542c060 100644 --- a/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in +++ b/program_info/org.prismlauncher.PrismLauncher.metainfo.xml.in @@ -4,7 +4,7 @@ org.prismlauncher.PrismLauncher.desktop Prism Launcher Prism Launcher Contributors - A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once + Custom Minecraft Launcher to easily manage multiple Minecraft installations at once CC0-1.0 GPL-3.0-only https://prismlauncher.org/ From f6511c601e1fd63a864f1681beef0f85adfe6f7e Mon Sep 17 00:00:00 2001 From: Ben Westover Date: Tue, 29 Oct 2024 18:24:19 +0000 Subject: [PATCH 35/39] Fix some typos Signed-off-by: Ben Westover --- .../auth/steps/MSADeviceCodeStep.cpp | 20 +++++++++---------- .../minecraft/auth/steps/MSADeviceCodeStep.h | 2 +- launcher/ui/java/InstallJavaDialog.cpp | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp index c283b153e..38ff90a47 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.cpp @@ -74,12 +74,12 @@ void MSADeviceCodeStep::perform() m_task->setAskRetry(false); m_task->addNetAction(m_request); - connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished); + connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAuthorizationFinished); m_task->start(); } -struct DeviceAutorizationResponse { +struct DeviceAuthorizationResponse { QString device_code; QString user_code; QString verification_uri; @@ -90,17 +90,17 @@ struct DeviceAutorizationResponse { QString error_description; }; -DeviceAutorizationResponse parseDeviceAutorizationResponse(const QByteArray& data) +DeviceAuthorizationResponse parseDeviceAuthorizationResponse(const QByteArray& data) { QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { - qWarning() << "Failed to parse device autorization response due to err:" << err.errorString(); + qWarning() << "Failed to parse device authorization response due to err:" << err.errorString(); return {}; } if (!doc.isObject()) { - qWarning() << "Device autorization response is not an object"; + qWarning() << "Device authorization response is not an object"; return {}; } auto obj = doc.object(); @@ -111,9 +111,9 @@ DeviceAutorizationResponse parseDeviceAutorizationResponse(const QByteArray& dat }; } -void MSADeviceCodeStep::deviceAutorizationFinished() +void MSADeviceCodeStep::deviceAuthorizationFinished() { - auto rsp = parseDeviceAutorizationResponse(*m_response); + auto rsp = parseDeviceAuthorizationResponse(*m_response); if (!rsp.error.isEmpty() || !rsp.error_description.isEmpty()) { qWarning() << "Device authorization failed:" << rsp.error; emit finished(AccountTaskState::STATE_FAILED_HARD, @@ -208,12 +208,12 @@ AuthenticationResponse parseAuthenticationResponse(const QByteArray& data) QJsonParseError err; QJsonDocument doc = QJsonDocument::fromJson(data, &err); if (err.error != QJsonParseError::NoError) { - qWarning() << "Failed to parse device autorization response due to err:" << err.errorString(); + qWarning() << "Failed to parse device authorization response due to err:" << err.errorString(); return {}; } if (!doc.isObject()) { - qWarning() << "Device autorization response is not an object"; + qWarning() << "Device authorization response is not an object"; return {}; } auto obj = doc.object(); @@ -274,4 +274,4 @@ void MSADeviceCodeStep::authenticationFinished() m_data->msaToken.refresh_token = rsp.refresh_token; m_data->msaToken.token = rsp.access_token; emit finished(AccountTaskState::STATE_WORKING, tr("Got")); -} \ No newline at end of file +} diff --git a/launcher/minecraft/auth/steps/MSADeviceCodeStep.h b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h index 616008def..7f755563f 100644 --- a/launcher/minecraft/auth/steps/MSADeviceCodeStep.h +++ b/launcher/minecraft/auth/steps/MSADeviceCodeStep.h @@ -58,7 +58,7 @@ class MSADeviceCodeStep : public AuthStep { void authorizeWithBrowser(QString url, QString code, int expiresIn); private slots: - void deviceAutorizationFinished(); + void deviceAuthorizationFinished(); void startPoolTimer(); void authenticateUser(); void authenticationFinished(); diff --git a/launcher/ui/java/InstallJavaDialog.cpp b/launcher/ui/java/InstallJavaDialog.cpp index d38670d05..dc43ea4c9 100644 --- a/launcher/ui/java/InstallJavaDialog.cpp +++ b/launcher/ui/java/InstallJavaDialog.cpp @@ -132,9 +132,9 @@ class InstallJavaPage : public QWidget, public BasePage { m_recommended_majors = majors; recommendedFilterChanged(); } - void setRecomend(bool recomend) + void setRecommend(bool recommend) { - m_recommend = recomend; + m_recommend = recommend; recommendedFilterChanged(); } void recommendedFilterChanged() @@ -202,7 +202,7 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget recommendedCheckBox->setCheckState(Qt::CheckState::Checked); connect(recommendedCheckBox, &QCheckBox::stateChanged, this, [this](int state) { for (BasePage* page : container->getPages()) { - pageCast(page)->setRecomend(state == Qt::Checked); + pageCast(page)->setRecommend(state == Qt::Checked); } }); @@ -261,7 +261,7 @@ InstallDialog::InstallDialog(const QString& uid, BaseInstance* instance, QWidget container->selectPage(page->id()); auto cast = pageCast(page); - cast->setRecomend(true); + cast->setRecommend(true); connect(cast, &InstallJavaPage::selectionChanged, this, [this, cast] { validate(cast); }); if (!recommendedJavas.isEmpty()) { cast->setRecommendedMajors(recommendedJavas); @@ -344,4 +344,4 @@ void InstallDialog::done(int result) } // namespace Java -#include "InstallJavaDialog.moc" \ No newline at end of file +#include "InstallJavaDialog.moc" From 43e4481b7093cb84c22cc54906fc2bf41208481e Mon Sep 17 00:00:00 2001 From: Michael Tyson Date: Tue, 23 Apr 2024 14:23:53 +1000 Subject: [PATCH 36/39] Support for launching from command line in offline mode This allows launching an offline instance with --offline --name=OfflineName. This is useful for playing split screen by creating two side-by-side instances, which is impossible online, unless one is using two separate paid accounts. With this PR, it makes it possible to launch from a script - otherwise, one has to launch manually, which is a pain, or create offline profiles for each instance, which interferes with some functionality like skins (my autistic son takes great issue with his skin not being visible, when using offline profiles!). Implementation is based on MultiMC, which supports this feature. See also https://github.com/PrismLauncher/PrismLauncher/issues/1059 for discussion. Signed-off-by: Michael Tyson --- launcher/Application.cpp | 28 +++++++++++++++++++++++----- launcher/Application.h | 5 ++++- launcher/LaunchController.cpp | 13 +++++++++---- launcher/LaunchController.h | 3 +++ 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ea749ca4c..5fa9da4ce 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -242,6 +242,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" }, { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, + { { "o", "offline" }, "Launch offline (only valid in combination with --launch)", "offline" }, + { { "n", "name" }, "When launching offline, use specified name (only makes sense in combination with --launch and --offline)", "name" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); @@ -257,6 +259,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_serverToJoin = parser.value("server"); m_worldToJoin = parser.value("world"); m_profileToUse = parser.value("profile"); + if (parser.isSet("offline")) { + m_offline = true; + m_offlineName = parser.value("name"); + } m_liveCheck = parser.isSet("alive"); m_instanceIdToShowWindowOf = parser.value("show"); @@ -271,8 +277,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // error if --launch is missing with --server or --profile - if (((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty()) || !m_profileToUse.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { - std::cerr << "--server and --profile can only be used in combination with --launch!" << std::endl; + if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline || !m_offlineName.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { + std::cerr << "--server, --profile, --offline and --name can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; } @@ -397,6 +403,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) if (!m_profileToUse.isEmpty()) { launch.args["profile"] = m_profileToUse; } + if (m_offline) { + launch.args["offline_enabled"] = "true"; + launch.args["offline_name"] = m_offlineName; + } m_peerInstance->sendMessage(launch.serialize(), timeout); } m_status = Application::Succeeded; @@ -1209,7 +1219,7 @@ void Application::performMainStartupAction() qDebug() << " Launching with account" << m_profileToUse; } - launch(inst, true, false, targetToJoin, accountToUse); + launch(inst, !m_offline, false, targetToJoin, accountToUse, m_offlineName); return; } } @@ -1308,6 +1318,8 @@ void Application::messageReceived(const QByteArray& message) QString server = received.args["server"]; QString world = received.args["world"]; QString profile = received.args["profile"]; + bool offline = received.args["offline_enabled"] == "true"; + QString offlineName = received.args["offline_name"]; InstancePtr instance; if (!id.isEmpty()) { @@ -1337,7 +1349,7 @@ void Application::messageReceived(const QByteArray& message) } } - launch(instance, true, false, serverObject, accountObject); + launch(instance, !offline, false, serverObject, accountObject, offlineName); } else { qWarning() << "Received invalid message" << message; } @@ -1375,7 +1387,12 @@ bool Application::openJsonEditor(const QString& filename) } } -bool Application::launch(InstancePtr instance, bool online, bool demo, MinecraftTarget::Ptr targetToJoin, MinecraftAccountPtr accountToUse) +bool Application::launch(InstancePtr instance, + bool online, + bool demo, + MinecraftTarget::Ptr targetToJoin, + MinecraftAccountPtr accountToUse, + const QString& offlineName) { if (m_updateRunning) { qDebug() << "Cannot launch instances while an update is running. Please try again when updates are completed."; @@ -1395,6 +1412,7 @@ bool Application::launch(InstancePtr instance, bool online, bool demo, Minecraft controller->setProfiler(profilers().value(instance->settings()->get("Profiler").toString(), nullptr).get()); controller->setTargetToJoin(targetToJoin); controller->setAccountToUse(accountToUse); + controller->setOfflineName(offlineName); if (window) { controller->setParentWidget(window); } else if (m_mainWindow) { diff --git a/launcher/Application.h b/launcher/Application.h index 692625f4a..5d672a148 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -211,7 +211,8 @@ class Application : public QApplication { bool online = true, bool demo = false, MinecraftTarget::Ptr targetToJoin = nullptr, - MinecraftAccountPtr accountToUse = nullptr); + MinecraftAccountPtr accountToUse = nullptr, + const QString &offlineName = QString()); bool kill(InstancePtr instance); void closeCurrentWindow(); @@ -300,6 +301,8 @@ class Application : public QApplication { QString m_serverToJoin; QString m_worldToJoin; QString m_profileToUse; + bool m_offline = false; + QString m_offlineName; bool m_liveCheck = false; QList m_urlsToImport; QString m_instanceIdToShowWindowOf; diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp index e736135fa..dcec62e9e 100644 --- a/launcher/LaunchController.cpp +++ b/launcher/LaunchController.cpp @@ -235,10 +235,15 @@ void LaunchController::login() if (!m_session->wants_online) { // we ask the user for a player name bool ok = false; - auto name = askOfflineName(m_session->player_name, m_session->demo, ok); - if (!ok) { - tryagain = false; - break; + QString name; + if (m_offlineName.isEmpty()) { + name = askOfflineName(m_session->player_name, m_session->demo, ok); + if (!ok) { + tryagain = false; + break; + } + } else { + name = m_offlineName; } m_session->MakeOffline(name); // offline flavored game from here :3 diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index 08fd19cae..82cd2e23d 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -56,6 +56,8 @@ class LaunchController : public Task { void setOnline(bool online) { m_online = online; } + void setOfflineName(const QString &offlineName) { m_offlineName = offlineName; } + void setDemo(bool demo) { m_demo = demo; } void setProfiler(BaseProfilerFactory* profiler) { m_profiler = profiler; } @@ -88,6 +90,7 @@ class LaunchController : public Task { private: BaseProfilerFactory* m_profiler = nullptr; bool m_online = true; + QString m_offlineName; bool m_demo = false; InstancePtr m_instance; QWidget* m_parentWidget = nullptr; From afa1e0599a5c0f1f5276f86b3709ff07c43879bc Mon Sep 17 00:00:00 2001 From: Michael Tyson Date: Tue, 23 Apr 2024 16:05:26 +1000 Subject: [PATCH 37/39] Improved formatting Signed-off-by: Michael Tyson --- launcher/Application.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 5fa9da4ce..e48ed73f0 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -243,7 +243,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" }, { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, { { "o", "offline" }, "Launch offline (only valid in combination with --launch)", "offline" }, - { { "n", "name" }, "When launching offline, use specified name (only makes sense in combination with --launch and --offline)", "name" }, + { { "n", "name" }, + "When launching offline, use specified name (only makes sense in combination with --launch and --offline)", + "name" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); From 56614444147afa9379877e591174a4ece7a54f32 Mon Sep 17 00:00:00 2001 From: Michael Tyson Date: Tue, 23 Apr 2024 16:53:29 +1000 Subject: [PATCH 38/39] =?UTF-8?q?Removed=20=E2=80=9C--name=E2=80=9D=20flag?= =?UTF-8?q?=20in=20favour=20of=20specifying=20offline=20player=20name=20vi?= =?UTF-8?q?a=20=E2=80=9C--offline=E2=80=9D=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Tyson --- launcher/Application.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e48ed73f0..7e85938ce 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -242,10 +242,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) { { "s", "server" }, "Join the specified server on launch (only valid in combination with --launch)", "address" }, { { "w", "world" }, "Join the specified world on launch (only valid in combination with --launch)", "world" }, { { "a", "profile" }, "Use the account specified by its profile name (only valid in combination with --launch)", "profile" }, - { { "o", "offline" }, "Launch offline (only valid in combination with --launch)", "offline" }, - { { "n", "name" }, - "When launching offline, use specified name (only makes sense in combination with --launch and --offline)", - "name" }, + { { "o", "offline" }, "Launch offline, with given player name (only valid in combination with --launch)", "offline" }, { "alive", "Write a small '" + liveCheckFile + "' file after the launcher starts" }, { { "I", "import" }, "Import instance or resource from specified local path or URL", "url" }, { "show", "Opens the window for the specified instance (by instance ID)", "show" } }); @@ -263,7 +260,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_profileToUse = parser.value("profile"); if (parser.isSet("offline")) { m_offline = true; - m_offlineName = parser.value("name"); + m_offlineName = parser.value("offline"); } m_liveCheck = parser.isSet("alive"); @@ -279,8 +276,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // error if --launch is missing with --server or --profile - if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline || !m_offlineName.isEmpty()) && m_instanceIdToLaunch.isEmpty()) { - std::cerr << "--server, --profile, --offline and --name can only be used in combination with --launch!" << std::endl; + if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) && m_instanceIdToLaunch.isEmpty()) { + std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; } From ced2f9d1a0c9ef65eb0f42efbf57c149362e88b9 Mon Sep 17 00:00:00 2001 From: Michael Tyson Date: Wed, 30 Oct 2024 12:51:34 +1100 Subject: [PATCH 39/39] Applied clang formatting Signed-off-by: Michael Tyson --- launcher/Application.cpp | 3 ++- launcher/Application.h | 2 +- launcher/LaunchController.h | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 7e85938ce..9a1ed440e 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -276,7 +276,8 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } // error if --launch is missing with --server or --profile - if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) && m_instanceIdToLaunch.isEmpty()) { + if ((!m_serverToJoin.isEmpty() || !m_worldToJoin.isEmpty() || !m_profileToUse.isEmpty() || m_offline) && + m_instanceIdToLaunch.isEmpty()) { std::cerr << "--server, --profile and --offline can only be used in combination with --launch!" << std::endl; m_status = Application::Failed; return; diff --git a/launcher/Application.h b/launcher/Application.h index 5d672a148..6b218f9f8 100644 --- a/launcher/Application.h +++ b/launcher/Application.h @@ -212,7 +212,7 @@ class Application : public QApplication { bool demo = false, MinecraftTarget::Ptr targetToJoin = nullptr, MinecraftAccountPtr accountToUse = nullptr, - const QString &offlineName = QString()); + const QString& offlineName = QString()); bool kill(InstancePtr instance); void closeCurrentWindow(); diff --git a/launcher/LaunchController.h b/launcher/LaunchController.h index 82cd2e23d..0abdeeb15 100644 --- a/launcher/LaunchController.h +++ b/launcher/LaunchController.h @@ -56,7 +56,7 @@ class LaunchController : public Task { void setOnline(bool online) { m_online = online; } - void setOfflineName(const QString &offlineName) { m_offlineName = offlineName; } + void setOfflineName(const QString& offlineName) { m_offlineName = offlineName; } void setDemo(bool demo) { m_demo = demo; }