From b30b88716e67de93ea1c97d9dfd02a41af5428f3 Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 13 Apr 2022 19:16:36 -0300 Subject: [PATCH 01/25] feat: add very early mod.toml packwiz support Also use it as a on-disk format for storing mod metadata. This will be used later on to make better mod managment. --- launcher/CMakeLists.txt | 8 +++ launcher/minecraft/mod/LocalModUpdateTask.cpp | 32 ++++++++++ launcher/minecraft/mod/LocalModUpdateTask.h | 26 ++++++++ launcher/modplatform/ModIndex.h | 31 ++++++++++ launcher/modplatform/flame/FlameModIndex.cpp | 1 + .../modrinth/ModrinthPackIndex.cpp | 1 + launcher/modplatform/packwiz/Packwiz.cpp | 60 +++++++++++++++++++ launcher/modplatform/packwiz/Packwiz.h | 43 +++++++++++++ 8 files changed, 202 insertions(+) create mode 100644 launcher/minecraft/mod/LocalModUpdateTask.cpp create mode 100644 launcher/minecraft/mod/LocalModUpdateTask.h create mode 100644 launcher/modplatform/packwiz/Packwiz.cpp create mode 100644 launcher/modplatform/packwiz/Packwiz.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 15534c71..b5c6fe91 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -331,6 +331,8 @@ set(MINECRAFT_SOURCES minecraft/mod/ModFolderLoadTask.cpp minecraft/mod/LocalModParseTask.h minecraft/mod/LocalModParseTask.cpp + minecraft/mod/LocalModUpdateTask.h + minecraft/mod/LocalModUpdateTask.cpp minecraft/mod/ResourcePackFolderModel.h minecraft/mod/ResourcePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.h @@ -543,6 +545,11 @@ set(MODPACKSCH_SOURCES modplatform/modpacksch/FTBPackManifest.cpp ) +set(PACKWIZ_SOURCES + modplatform/packwiz/Packwiz.h + modplatform/packwiz/Packwiz.cpp +) + set(TECHNIC_SOURCES modplatform/technic/SingleZipPackInstallTask.h modplatform/technic/SingleZipPackInstallTask.cpp @@ -596,6 +603,7 @@ set(LOGIC_SOURCES ${FLAME_SOURCES} ${MODRINTH_SOURCES} ${MODPACKSCH_SOURCES} + ${PACKWIZ_SOURCES} ${TECHNIC_SOURCES} ${ATLAUNCHER_SOURCES} ) diff --git a/launcher/minecraft/mod/LocalModUpdateTask.cpp b/launcher/minecraft/mod/LocalModUpdateTask.cpp new file mode 100644 index 00000000..0f48217b --- /dev/null +++ b/launcher/minecraft/mod/LocalModUpdateTask.cpp @@ -0,0 +1,32 @@ +#include "LocalModUpdateTask.h" + +#include + +#include "FileSystem.h" +#include "modplatform/packwiz/Packwiz.h" + +LocalModUpdateTask::LocalModUpdateTask(QDir mods_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version) + : m_mod(mod), m_mod_version(mod_version) +{ + // Ensure a '.index' folder exists in the mods folder, and create it if it does not + m_index_dir = { QString("%1/.index").arg(mods_dir.absolutePath()) }; + if (!FS::ensureFolderPathExists(m_index_dir.path())) { + emitFailed(QString("Unable to create index for mod %1!").arg(m_mod.name)); + } +} + +void LocalModUpdateTask::executeTask() +{ + setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name)); + + auto pw_mod = Packwiz::createModFormat(m_index_dir, m_mod, m_mod_version); + Packwiz::updateModIndex(m_index_dir, pw_mod); + + emitSucceeded(); +} + +bool LocalModUpdateTask::abort() +{ + emitAborted(); + return true; +} diff --git a/launcher/minecraft/mod/LocalModUpdateTask.h b/launcher/minecraft/mod/LocalModUpdateTask.h new file mode 100644 index 00000000..866089e9 --- /dev/null +++ b/launcher/minecraft/mod/LocalModUpdateTask.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "tasks/Task.h" +#include "modplatform/ModIndex.h" + +class LocalModUpdateTask : public Task { + Q_OBJECT + public: + using Ptr = shared_qobject_ptr; + + explicit LocalModUpdateTask(QDir mods_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version); + + bool canAbort() const override { return true; } + bool abort() override; + + protected slots: + //! Entry point for tasks. + void executeTask() override; + + private: + QDir m_index_dir; + ModPlatform::IndexedPack& m_mod; + ModPlatform::IndexedVersion& m_mod_version; +}; diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 7e1cf254..9c9ba99f 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -8,6 +8,35 @@ namespace ModPlatform { +enum class Provider{ + MODRINTH, + FLAME +}; + +class ProviderCapabilities { + public: + static QString hashType(Provider p) + { + switch(p){ + case Provider::MODRINTH: + return "sha256"; + case Provider::FLAME: + return "murmur2"; + } + return ""; + } + static QString providerName(Provider p) + { + switch(p){ + case Provider::MODRINTH: + return "modrinth"; + case Provider::FLAME: + return "curseforge"; + } + return ""; + } +}; + struct ModpackAuthor { QString name; QString url; @@ -26,6 +55,7 @@ struct IndexedVersion { struct IndexedPack { QVariant addonId; + Provider provider; QString name; QString description; QList authors; @@ -40,3 +70,4 @@ struct IndexedPack { } // namespace ModPlatform Q_DECLARE_METATYPE(ModPlatform::IndexedPack) +Q_DECLARE_METATYPE(ModPlatform::Provider) diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index ba0824cf..45f02b71 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -9,6 +9,7 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireInteger(obj, "id"); + pack.provider = ModPlatform::Provider::FLAME; pack.name = Json::requireString(obj, "name"); pack.websiteUrl = Json::ensureString(Json::ensureObject(obj, "links"), "websiteUrl", ""); pack.description = Json::ensureString(obj, "summary", ""); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index f7fa9864..6c8659dc 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -28,6 +28,7 @@ static ModrinthAPI api; void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireString(obj, "project_id"); + pack.provider = ModPlatform::Provider::MODRINTH; pack.name = Json::requireString(obj, "title"); QString slug = Json::ensureString(obj, "slug", ""); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp new file mode 100644 index 00000000..ff86a8a9 --- /dev/null +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -0,0 +1,60 @@ +#include "Packwiz.h" + +#include "modplatform/ModIndex.h" + +#include +#include +#include + +auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod +{ + Mod mod; + + mod.name = mod_pack.name; + mod.filename = mod_version.fileName; + + mod.url = mod_version.downloadUrl; + mod.hash_format = ModPlatform::ProviderCapabilities::hashType(mod_pack.provider); + mod.hash = ""; // FIXME + + mod.provider = mod_pack.provider; + mod.file_id = mod_pack.addonId; + mod.project_id = mod_version.fileId; + + return mod; +} + +void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) +{ + // Ensure the corresponding mod's info exists, and create it if not + auto index_file_name = QString("%1.toml").arg(mod.name); + QFile index_file(index_dir.absoluteFilePath(index_file_name)); + + // There's already data on there! + if (index_file.exists()) { index_file.remove(); } + + if (!index_file.open(QIODevice::ReadWrite)) { + qCritical() << "Could not open file " << index_file_name << "!"; + return; + } + + // Put TOML data into the file + QTextStream in_stream(&index_file); + auto addToStream = [&in_stream](QString&& key, QString value) { in_stream << QString("%1 = \"%2\"\n").arg(key, value); }; + + { + addToStream("name", mod.name); + addToStream("filename", mod.filename); + addToStream("side", mod.side); + + in_stream << QString("\n[download]\n"); + addToStream("url", mod.url.toString()); + addToStream("hash-format", mod.hash_format); + addToStream("hash", mod.hash); + + in_stream << QString("\n[update]\n"); + in_stream << QString("[update.%1]\n").arg(ModPlatform::ProviderCapabilities::providerName(mod.provider)); + addToStream("file-id", mod.file_id.toString()); + addToStream("project-id", mod.project_id.toString()); + } +} diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h new file mode 100644 index 00000000..64b95e7a --- /dev/null +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace ModPlatform { +enum class Provider; +class IndexedPack; +class IndexedVersion; +} // namespace ModPlatform + +class QDir; + +class Packwiz { + public: + struct Mod { + QString name; + QString filename; + // FIXME: make side an enum + QString side = "both"; + + // [download] + QUrl url; + // FIXME: make hash-format an enum + QString hash_format; + QString hash; + + // [update] + ModPlatform::Provider provider; + QVariant file_id; + QVariant project_id; + }; + + /* Generates the object representing the information in a mod.toml file via its common representation in the launcher */ + static auto createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> 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); +}; From c86c719e1a09be2dc25ffd26278076566672e3b5 Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 13 Apr 2022 19:18:28 -0300 Subject: [PATCH 02/25] feat: add mod index updating to ModDownloadTask This makes ModDownloadTask into a SequentialTask with 2 subtasks: Downloading the mod files and updating the index with the new information. The index updating is done first so that, in the future, we can prompt the user before download if, for instance, we discover there's another version already installed. --- launcher/ModDownloadTask.cpp | 25 +++++++++++----------- launcher/ModDownloadTask.h | 26 +++++++++++------------ launcher/ui/pages/modplatform/ModPage.cpp | 2 +- 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp index 08a02d29..e5766435 100644 --- a/launcher/ModDownloadTask.cpp +++ b/launcher/ModDownloadTask.cpp @@ -1,24 +1,28 @@ #include "ModDownloadTask.h" #include "Application.h" +#include "minecraft/mod/LocalModUpdateTask.h" -ModDownloadTask::ModDownloadTask(const QUrl sourceUrl,const QString filename, const std::shared_ptr mods) -: m_sourceUrl(sourceUrl), mods(mods), filename(filename) { -} +ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods) + : m_mod(mod), m_mod_version(version), mods(mods) +{ + m_update_task.reset(new LocalModUpdateTask(mods->dir(), m_mod, m_mod_version)); -void ModDownloadTask::executeTask() { - setStatus(tr("Downloading mod:\n%1").arg(m_sourceUrl.toString())); + addTask(m_update_task); m_filesNetJob.reset(new NetJob(tr("Mod download"), APPLICATION->network())); - m_filesNetJob->addNetAction(Net::Download::makeFile(m_sourceUrl, mods->dir().absoluteFilePath(filename))); + m_filesNetJob->setStatus(tr("Downloading mod:\n%1").arg(m_mod_version.downloadUrl)); + + m_filesNetJob->addNetAction(Net::Download::makeFile(m_mod_version.downloadUrl, mods->dir().absoluteFilePath(getFilename()))); connect(m_filesNetJob.get(), &NetJob::succeeded, this, &ModDownloadTask::downloadSucceeded); connect(m_filesNetJob.get(), &NetJob::progress, this, &ModDownloadTask::downloadProgressChanged); connect(m_filesNetJob.get(), &NetJob::failed, this, &ModDownloadTask::downloadFailed); - m_filesNetJob->start(); + + addTask(m_filesNetJob); + } void ModDownloadTask::downloadSucceeded() { - emitSucceeded(); m_filesNetJob.reset(); } @@ -32,8 +36,3 @@ void ModDownloadTask::downloadProgressChanged(qint64 current, qint64 total) { emit progress(current, total); } - -bool ModDownloadTask::abort() { - return m_filesNetJob->abort(); -} - diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h index ddada5a2..d292dfbb 100644 --- a/launcher/ModDownloadTask.h +++ b/launcher/ModDownloadTask.h @@ -1,28 +1,26 @@ #pragma once + #include "QObjectPtr.h" -#include "tasks/Task.h" +#include "minecraft/mod/LocalModUpdateTask.h" +#include "modplatform/ModIndex.h" +#include "tasks/SequentialTask.h" #include "minecraft/mod/ModFolderModel.h" #include "net/NetJob.h" #include - -class ModDownloadTask : public Task { +class ModDownloadTask : public SequentialTask { Q_OBJECT public: - explicit ModDownloadTask(const QUrl sourceUrl, const QString filename, const std::shared_ptr mods); - const QString& getFilename() const { return filename; } - -public slots: - bool abort() override; -protected: - //! Entry point for tasks. - void executeTask() override; + explicit ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods); + const QString& getFilename() const { return m_mod_version.fileName; } private: - QUrl m_sourceUrl; - NetJob::Ptr m_filesNetJob; + ModPlatform::IndexedPack m_mod; + ModPlatform::IndexedVersion m_mod_version; const std::shared_ptr mods; - const QString filename; + + NetJob::Ptr m_filesNetJob; + LocalModUpdateTask::Ptr m_update_task; void downloadProgressChanged(qint64 current, qint64 total); diff --git a/launcher/ui/pages/modplatform/ModPage.cpp b/launcher/ui/pages/modplatform/ModPage.cpp index ad36cf2f..5020d44c 100644 --- a/launcher/ui/pages/modplatform/ModPage.cpp +++ b/launcher/ui/pages/modplatform/ModPage.cpp @@ -150,7 +150,7 @@ void ModPage::onModSelected() if (dialog->isModSelected(current.name, version.fileName)) { dialog->removeSelectedMod(current.name); } else { - dialog->addSelectedMod(current.name, new ModDownloadTask(version.downloadUrl, version.fileName, dialog->mods)); + dialog->addSelectedMod(current.name, new ModDownloadTask(current, version, dialog->mods)); } updateSelectionButton(); From eaa5ce446765ef4305a1462d68e278b0797966ee Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 13 Apr 2022 19:23:12 -0300 Subject: [PATCH 03/25] feat(ui): adapt SequentialTask to nested SequentialTasks --- launcher/modplatform/packwiz/Packwiz.h | 5 ++--- launcher/tasks/SequentialTask.cpp | 12 +++++++++--- launcher/tasks/SequentialTask.h | 9 +++------ launcher/tasks/Task.h | 1 + 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 64b95e7a..9c90f7de 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -1,13 +1,12 @@ #pragma once +#include "modplatform/ModIndex.h" + #include #include #include namespace ModPlatform { -enum class Provider; -class IndexedPack; -class IndexedVersion; } // namespace ModPlatform class QDir; diff --git a/launcher/tasks/SequentialTask.cpp b/launcher/tasks/SequentialTask.cpp index 1573e476..2d50c299 100644 --- a/launcher/tasks/SequentialTask.cpp +++ b/launcher/tasks/SequentialTask.cpp @@ -53,12 +53,18 @@ void SequentialTask::startNext() return; } Task::Ptr next = m_queue[m_currentIndex]; + connect(next.get(), SIGNAL(failed(QString)), this, SLOT(subTaskFailed(QString))); - connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString))); - connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64))); connect(next.get(), SIGNAL(succeeded()), this, SLOT(startNext())); + connect(next.get(), SIGNAL(status(QString)), this, SLOT(subTaskStatus(QString))); + connect(next.get(), SIGNAL(stepStatus(QString)), this, SLOT(subTaskStatus(QString))); + + connect(next.get(), SIGNAL(progress(qint64, qint64)), this, SLOT(subTaskProgress(qint64, qint64))); + setStatus(tr("Executing task %1 out of %2").arg(m_currentIndex + 1).arg(m_queue.size())); + setStepStatus(next->isMultiStep() ? next->getStepStatus() : next->getStatus()); + next->start(); } @@ -68,7 +74,7 @@ void SequentialTask::subTaskFailed(const QString& msg) } void SequentialTask::subTaskStatus(const QString& msg) { - setStepStatus(m_queue[m_currentIndex]->getStatus()); + setStepStatus(msg); } void SequentialTask::subTaskProgress(qint64 current, qint64 total) { diff --git a/launcher/tasks/SequentialTask.h b/launcher/tasks/SequentialTask.h index 5b3c0111..e10cb6f7 100644 --- a/launcher/tasks/SequentialTask.h +++ b/launcher/tasks/SequentialTask.h @@ -32,13 +32,10 @@ slots: void subTaskStatus(const QString &msg); void subTaskProgress(qint64 current, qint64 total); -signals: - void stepStatus(QString status); +protected: + void setStepStatus(QString status) { m_step_status = status; emit stepStatus(status); }; -private: - void setStepStatus(QString status) { m_step_status = status; }; - -private: +protected: QString m_name; QString m_step_status; diff --git a/launcher/tasks/Task.h b/launcher/tasks/Task.h index f7765c3d..aafaf68c 100644 --- a/launcher/tasks/Task.h +++ b/launcher/tasks/Task.h @@ -92,6 +92,7 @@ class Task : public QObject { void aborted(); void failed(QString reason); void status(QString status); + void stepStatus(QString status); public slots: virtual void start(); From 8e4438b375ee904aa8225b569899355372e5987c Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 13 Apr 2022 21:25:08 -0300 Subject: [PATCH 04/25] feat: add parser for current impl of packwiz mod.toml This reads a local mod.toml file and extract information from it. Using C libs in C++ is kind of a pain tho :( --- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 90 ++++++++++++++++++++++++ launcher/modplatform/packwiz/Packwiz.h | 5 ++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 9c9ba99f..c5329772 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -25,7 +25,7 @@ class ProviderCapabilities { } return ""; } - static QString providerName(Provider p) + static const char* providerName(Provider p) { switch(p){ case Provider::MODRINTH: diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index ff86a8a9..58bead82 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -1,6 +1,7 @@ #include "Packwiz.h" #include "modplatform/ModIndex.h" +#include "toml.h" #include #include @@ -58,3 +59,92 @@ void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) addToStream("project-id", mod.project_id.toString()); } } + +auto Packwiz::getIndexForMod(QDir& index_dir, QString mod_name) -> Mod +{ + Mod mod; + + auto index_file_name = QString("%1.toml").arg(mod_name); + QFile index_file(index_dir.absoluteFilePath(index_file_name)); + + if (!index_file.exists()) { return mod; } + if (!index_file.open(QIODevice::ReadOnly)) { return mod; } + + toml_table_t* table; + + char errbuf[200]; + table = toml_parse(index_file.readAll().data(), errbuf, sizeof(errbuf)); + + index_file.close(); + + if (!table) { + qCritical() << QString("Could not open file %1").arg(index_file_name); + return mod; + } + + // Helper function for extracting data from the TOML file + auto stringEntry = [&](toml_table_t* parent, const char* entry_name) -> QString { + toml_datum_t var = toml_string_in(parent, entry_name); + if (!var.ok) { + qCritical() << QString("Failed to read property '%1' in mod metadata.").arg(entry_name); + return {}; + } + + QString tmp = var.u.s; + free(var.u.s); + + return tmp; + }; + + { // Basic info + mod.name = stringEntry(table, "name"); + // Basic sanity check + if (mod.name != mod_name) { + qCritical() << QString("Name mismatch in mod metadata:\nExpected:%1\nGot:%2").arg(mod_name, mod.name); + return {}; + } + + mod.filename = stringEntry(table, "filename"); + mod.side = stringEntry(table, "side"); + } + + { // [download] info + toml_table_t* download_table = toml_table_in(table, "download"); + if (!download_table) { + qCritical() << QString("No [download] section found on mod metadata!"); + return {}; + } + + mod.url = stringEntry(download_table, "url"); + mod.hash_format = stringEntry(download_table, "hash-format"); + mod.hash = stringEntry(download_table, "hash"); + } + + { // [update] info + using ProviderCaps = ModPlatform::ProviderCapabilities; + using Provider = ModPlatform::Provider; + + toml_table_t* update_table = toml_table_in(table, "update"); + if (!update_table) { + qCritical() << QString("No [update] section found on mod metadata!"); + return {}; + } + + toml_table_t* mod_provider_table; + if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::FLAME)))) { + mod.provider = Provider::FLAME; + } else if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::MODRINTH)))) { + mod.provider = Provider::MODRINTH; + } else { + qCritical() << "No mod provider on mod metadata!"; + return {}; + } + + mod.file_id = stringEntry(mod_provider_table, "file-id"); + mod.project_id = stringEntry(mod_provider_table, "project-id"); + } + + toml_free(table); + + return mod; +} diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 9c90f7de..08edaab9 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -39,4 +39,9 @@ class Packwiz { * 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); + + /* Gets the metadata for a mod with a particular name. + * If the mod doesn't have a metadata, it simply returns an empty Mod object. + * */ + static auto getIndexForMod(QDir& index_dir, QString mod_name) -> Mod; }; From e93b9560b5137a5ee7acdc34c0f74992aa02aad6 Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 14 Apr 2022 22:02:41 -0300 Subject: [PATCH 05/25] feat: add method to delete mod metadata Also moves indexDir setting from LocalModUpdateTask -> ModFolderModel --- launcher/ModDownloadTask.cpp | 2 +- launcher/minecraft/mod/LocalModUpdateTask.cpp | 7 ++- launcher/minecraft/mod/ModFolderModel.h | 7 ++- launcher/modplatform/packwiz/Packwiz.cpp | 44 ++++++++++++++----- launcher/modplatform/packwiz/Packwiz.h | 5 ++- 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp index e5766435..ad1e64e3 100644 --- a/launcher/ModDownloadTask.cpp +++ b/launcher/ModDownloadTask.cpp @@ -5,7 +5,7 @@ ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods) : m_mod(mod), m_mod_version(version), mods(mods) { - m_update_task.reset(new LocalModUpdateTask(mods->dir(), m_mod, m_mod_version)); + m_update_task.reset(new LocalModUpdateTask(mods->indexDir(), m_mod, m_mod_version)); addTask(m_update_task); diff --git a/launcher/minecraft/mod/LocalModUpdateTask.cpp b/launcher/minecraft/mod/LocalModUpdateTask.cpp index 0f48217b..63f5cf9a 100644 --- a/launcher/minecraft/mod/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/LocalModUpdateTask.cpp @@ -5,12 +5,11 @@ #include "FileSystem.h" #include "modplatform/packwiz/Packwiz.h" -LocalModUpdateTask::LocalModUpdateTask(QDir mods_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version) - : m_mod(mod), m_mod_version(mod_version) +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) { // Ensure a '.index' folder exists in the mods folder, and create it if it does not - m_index_dir = { QString("%1/.index").arg(mods_dir.absolutePath()) }; - if (!FS::ensureFolderPathExists(m_index_dir.path())) { + if (!FS::ensureFolderPathExists(index_dir.path())) { emitFailed(QString("Unable to create index for mod %1!").arg(m_mod.name)); } } diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index 62c504df..f8ad4ca8 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -108,11 +108,16 @@ public: bool isValid(); - QDir dir() + QDir& dir() { return m_dir; } + QDir indexDir() + { + return { QString("%1/.index").arg(dir().absolutePath()) }; + } + const QList & allMods() { return mods; diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 58bead82..bfadf7cb 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -7,6 +7,12 @@ #include #include +// Helpers +static inline QString indexFileName(QString const& mod_name) +{ + return QString("%1.toml").arg(mod_name); +} + auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -28,14 +34,13 @@ auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pac void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) { // Ensure the corresponding mod's info exists, and create it if not - auto index_file_name = QString("%1.toml").arg(mod.name); - QFile index_file(index_dir.absoluteFilePath(index_file_name)); + QFile index_file(index_dir.absoluteFilePath(indexFileName(mod.name))); // There's already data on there! if (index_file.exists()) { index_file.remove(); } if (!index_file.open(QIODevice::ReadWrite)) { - qCritical() << "Could not open file " << index_file_name << "!"; + qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); return; } @@ -60,15 +65,34 @@ void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) } } -auto Packwiz::getIndexForMod(QDir& index_dir, QString mod_name) -> Mod +void Packwiz::deleteModIndex(QDir& index_dir, QString& mod_name) +{ + QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name))); + + if(!index_file.exists()){ + qWarning() << QString("Tried to delete non-existent mod metadata for %1!").arg(mod_name); + return; + } + + if(!index_file.remove()){ + qWarning() << QString("Failed to remove metadata for mod %1!").arg(mod_name); + } +} + +auto Packwiz::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod { Mod mod; - auto index_file_name = QString("%1.toml").arg(mod_name); - QFile index_file(index_dir.absoluteFilePath(index_file_name)); + QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name))); - if (!index_file.exists()) { return mod; } - if (!index_file.open(QIODevice::ReadOnly)) { return mod; } + if (!index_file.exists()) { + qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(mod_name); + return mod; + } + if (!index_file.open(QIODevice::ReadOnly)) { + qWarning() << QString("Failed to open mod metadata for %1").arg(mod_name); + return mod; + } toml_table_t* table; @@ -78,7 +102,7 @@ auto Packwiz::getIndexForMod(QDir& index_dir, QString mod_name) -> Mod index_file.close(); if (!table) { - qCritical() << QString("Could not open file %1").arg(index_file_name); + qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); return mod; } @@ -136,7 +160,7 @@ auto Packwiz::getIndexForMod(QDir& index_dir, QString mod_name) -> Mod } else if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::MODRINTH)))) { mod.provider = Provider::MODRINTH; } else { - qCritical() << "No mod provider on mod metadata!"; + qCritical() << QString("No mod provider on mod metadata!"); return {}; } diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 08edaab9..541059d0 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -40,8 +40,11 @@ class Packwiz { * */ static void updateModIndex(QDir& index_dir, Mod& mod); + /* Deletes the metadata for the mod with the given name. If the metadata doesn't exist, it does nothing. */ + static void deleteModIndex(QDir& index_dir, QString& mod_name); + /* Gets the metadata for a mod with a particular name. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QString mod_name) -> Mod; + static auto getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod; }; From fcfb2cfc3da9a8f897063db05fdf3aebc41a59ae Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 15 Apr 2022 00:24:57 -0300 Subject: [PATCH 06/25] feat: use mod metadata for getting mod information For now this doesn't mean much, but it will help when we need data exclusive from the metadata, such as addon id and mod provider. Also removes the metadata when the mod is deleted, and make the Mod.h file a little more pleasing to look at :) --- launcher/minecraft/mod/Mod.cpp | 33 ++++++++- launcher/minecraft/mod/Mod.h | 73 +++++++------------- launcher/minecraft/mod/ModFolderLoadTask.cpp | 31 +++++++-- launcher/minecraft/mod/ModFolderLoadTask.h | 4 +- launcher/minecraft/mod/ModFolderModel.cpp | 9 ++- 5 files changed, 90 insertions(+), 60 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index b6bff29b..59f4d83b 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -33,6 +33,30 @@ Mod::Mod(const QFileInfo &file) m_changedDateTime = file.lastModified(); } +Mod::Mod(const QDir& mods_dir, const Packwiz::Mod& metadata) + : m_file(mods_dir.absoluteFilePath(metadata.filename)) + // It is weird, but name is not reliable for comparing with the JAR files name + // FIXME: Maybe use hash when implemented? + , m_mmc_id(metadata.filename) + , m_name(metadata.name) +{ + if(m_file.isDir()){ + m_type = MOD_FOLDER; + } + else{ + if (metadata.filename.endsWith(".zip") || metadata.filename.endsWith(".jar")) + m_type = MOD_ZIPFILE; + else if (metadata.filename.endsWith(".litemod")) + m_type = MOD_LITEMOD; + else + m_type = MOD_SINGLEFILE; + } + + m_from_metadata = true; + m_enabled = true; + m_changedDateTime = m_file.lastModified(); +} + void Mod::repath(const QFileInfo &file) { m_file = file; @@ -101,13 +125,18 @@ bool Mod::enable(bool value) if (!foo.rename(path)) return false; } - repath(QFileInfo(path)); + if(!fromMetadata()) + repath(QFileInfo(path)); + m_enabled = value; return true; } -bool Mod::destroy() +bool Mod::destroy(QDir& index_dir) { + // Delete metadata + Packwiz::deleteModIndex(index_dir, m_name); + m_type = MOD_UNKNOWN; return FS::deletePath(m_file.filePath()); } diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 921faeb1..c9fd5813 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -14,14 +14,14 @@ */ #pragma once -#include + #include +#include #include #include #include "ModDetails.h" - - +#include "modplatform/packwiz/Packwiz.h" class Mod { @@ -32,65 +32,41 @@ public: MOD_ZIPFILE, //!< The mod is a zip file containing the mod's class files. MOD_SINGLEFILE, //!< The mod is a single file (not a zip file). MOD_FOLDER, //!< The mod is in a folder on the filesystem. - MOD_LITEMOD, //!< The mod is a litemod + MOD_LITEMOD, //!< The mod is a litemod }; Mod() = default; Mod(const QFileInfo &file); + explicit Mod(const QDir& mods_dir, const Packwiz::Mod& metadata); - QFileInfo filename() const - { - return m_file; - } - QString mmc_id() const - { - return m_mmc_id; - } - ModType type() const - { - return m_type; - } - bool valid() - { - return m_type != MOD_UNKNOWN; - } + QFileInfo filename() const { return m_file; } + QDateTime dateTimeChanged() const { return m_changedDateTime; } + QString mmc_id() const { return m_mmc_id; } + ModType type() const { return m_type; } + bool fromMetadata() const { return m_from_metadata; } + bool enabled() const { return m_enabled; } - QDateTime dateTimeChanged() const - { - return m_changedDateTime; - } + bool valid() const { return m_type != MOD_UNKNOWN; } - bool enabled() const - { - return m_enabled; - } - - const ModDetails &details() const; - - QString name() const; - QString version() const; - QString homeurl() const; + const ModDetails& details() const; + QString name() const; + QString version() const; + QString homeurl() const; QString description() const; QStringList authors() const; bool enable(bool value); // delete all the files of this mod - bool destroy(); + bool destroy(QDir& index_dir); // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) void repath(const QFileInfo &file); - bool shouldResolve() { - return !m_resolving && !m_resolved; - } - bool isResolving() { - return m_resolving; - } - int resolutionTicket() - { - return m_resolutionTicket; - } + bool shouldResolve() const { return !m_resolving && !m_resolved; } + bool isResolving() const { return m_resolving; } + int resolutionTicket() const { return m_resolutionTicket; } + void setResolving(bool resolving, int resolutionTicket) { m_resolving = resolving; m_resolutionTicket = resolutionTicket; @@ -104,12 +80,15 @@ public: protected: QFileInfo m_file; QDateTime m_changedDateTime; + QString m_mmc_id; QString m_name; + ModType m_type = MOD_UNKNOWN; + bool m_from_metadata = false; + std::shared_ptr m_localDetails; + bool m_enabled = true; bool m_resolving = false; bool m_resolved = false; int m_resolutionTicket = 0; - ModType m_type = MOD_UNKNOWN; - std::shared_ptr m_localDetails; }; diff --git a/launcher/minecraft/mod/ModFolderLoadTask.cpp b/launcher/minecraft/mod/ModFolderLoadTask.cpp index 88349877..fd4d6008 100644 --- a/launcher/minecraft/mod/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/ModFolderLoadTask.cpp @@ -1,18 +1,35 @@ #include "ModFolderLoadTask.h" #include -ModFolderLoadTask::ModFolderLoadTask(QDir dir) : - m_dir(dir), m_result(new Result()) +#include "modplatform/packwiz/Packwiz.h" + +ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) : + m_mods_dir(mods_dir), m_index_dir(index_dir), m_result(new Result()) { } void ModFolderLoadTask::run() { - m_dir.refresh(); - for (auto entry : m_dir.entryInfoList()) - { - Mod m(entry); - m_result->mods[m.mmc_id()] = m; + // Read metadata first + m_index_dir.refresh(); + for(auto entry : m_index_dir.entryList()){ + // QDir::Filter::NoDotAndDotDot seems to exclude all files for some reason... + if(entry == "." || entry == "..") + continue; + + entry.chop(5); // Remove .toml at the end + Mod mod(m_mods_dir, Packwiz::getIndexForMod(m_index_dir, entry)); + m_result->mods[mod.mmc_id()] = mod; } + + // Read JAR files that don't have metadata + m_mods_dir.refresh(); + for (auto entry : m_mods_dir.entryInfoList()) + { + Mod mod(entry); + if(!m_result->mods.contains(mod.mmc_id())) + m_result->mods[mod.mmc_id()] = mod; + } + emit succeeded(); } diff --git a/launcher/minecraft/mod/ModFolderLoadTask.h b/launcher/minecraft/mod/ModFolderLoadTask.h index 8d720e65..c869f083 100644 --- a/launcher/minecraft/mod/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/ModFolderLoadTask.h @@ -19,11 +19,11 @@ public: } public: - ModFolderLoadTask(QDir dir); + ModFolderLoadTask(QDir& mods_dir, QDir& index_dir); void run(); signals: void succeeded(); private: - QDir m_dir; + QDir& m_mods_dir, m_index_dir; ResultPtr m_result; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index f0c53c39..615cfc0c 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -79,10 +79,14 @@ bool ModFolderModel::update() return true; } - auto task = new ModFolderLoadTask(m_dir); + auto index_dir = indexDir(); + auto task = new ModFolderLoadTask(dir(), index_dir); + m_update = task->result(); + QThreadPool *threadPool = QThreadPool::globalInstance(); connect(task, &ModFolderLoadTask::succeeded, this, &ModFolderModel::finishUpdate); + threadPool->start(task); return true; } @@ -334,7 +338,8 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes) for (auto i: indexes) { Mod &m = mods[i.row()]; - m.destroy(); + auto index_dir = indexDir(); + m.destroy(index_dir); } return true; } From 5a34e8fd7c913bc138e1606baf9df2cd1a64baed Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 15 Apr 2022 20:35:17 -0300 Subject: [PATCH 07/25] refactor: move mod tasks to their own subfolder Makes the launcher/minecraft/mod/ folder a little more organized. --- launcher/CMakeLists.txt | 12 ++++++------ launcher/ModDownloadTask.cpp | 3 ++- launcher/ModDownloadTask.h | 13 +++++++------ launcher/minecraft/mod/ModFolderModel.cpp | 14 ++++++++------ launcher/minecraft/mod/ModFolderModel.h | 4 ++-- .../mod/{ => tasks}/LocalModParseTask.cpp | 0 .../minecraft/mod/{ => tasks}/LocalModParseTask.h | 8 +++++--- .../mod/{ => tasks}/LocalModUpdateTask.cpp | 0 .../minecraft/mod/{ => tasks}/LocalModUpdateTask.h | 0 .../mod/{ => tasks}/ModFolderLoadTask.cpp | 0 .../minecraft/mod/{ => tasks}/ModFolderLoadTask.h | 7 ++++--- 11 files changed, 34 insertions(+), 27 deletions(-) rename launcher/minecraft/mod/{ => tasks}/LocalModParseTask.cpp (100%) rename launcher/minecraft/mod/{ => tasks}/LocalModParseTask.h (90%) rename launcher/minecraft/mod/{ => tasks}/LocalModUpdateTask.cpp (100%) rename launcher/minecraft/mod/{ => tasks}/LocalModUpdateTask.h (100%) rename launcher/minecraft/mod/{ => tasks}/ModFolderLoadTask.cpp (100%) rename launcher/minecraft/mod/{ => tasks}/ModFolderLoadTask.h (94%) diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b5c6fe91..b6df2851 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -327,16 +327,16 @@ set(MINECRAFT_SOURCES minecraft/mod/ModDetails.h minecraft/mod/ModFolderModel.h minecraft/mod/ModFolderModel.cpp - minecraft/mod/ModFolderLoadTask.h - minecraft/mod/ModFolderLoadTask.cpp - minecraft/mod/LocalModParseTask.h - minecraft/mod/LocalModParseTask.cpp - minecraft/mod/LocalModUpdateTask.h - minecraft/mod/LocalModUpdateTask.cpp minecraft/mod/ResourcePackFolderModel.h minecraft/mod/ResourcePackFolderModel.cpp minecraft/mod/TexturePackFolderModel.h minecraft/mod/TexturePackFolderModel.cpp + minecraft/mod/tasks/ModFolderLoadTask.h + minecraft/mod/tasks/ModFolderLoadTask.cpp + minecraft/mod/tasks/LocalModParseTask.h + minecraft/mod/tasks/LocalModParseTask.cpp + minecraft/mod/tasks/LocalModUpdateTask.h + minecraft/mod/tasks/LocalModUpdateTask.cpp # Assets minecraft/AssetsUtils.h diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp index ad1e64e3..52de9c94 100644 --- a/launcher/ModDownloadTask.cpp +++ b/launcher/ModDownloadTask.cpp @@ -1,6 +1,7 @@ #include "ModDownloadTask.h" + #include "Application.h" -#include "minecraft/mod/LocalModUpdateTask.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods) : m_mod(mod), m_mod_version(version), mods(mods) diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h index d292dfbb..5eaee187 100644 --- a/launcher/ModDownloadTask.h +++ b/launcher/ModDownloadTask.h @@ -1,12 +1,13 @@ #pragma once -#include "QObjectPtr.h" -#include "minecraft/mod/LocalModUpdateTask.h" -#include "modplatform/ModIndex.h" -#include "tasks/SequentialTask.h" -#include "minecraft/mod/ModFolderModel.h" -#include "net/NetJob.h" #include +#include "QObjectPtr.h" +#include "minecraft/mod/ModFolderModel.h" +#include "modplatform/ModIndex.h" +#include "net/NetJob.h" + +#include "tasks/SequentialTask.h" +#include "minecraft/mod/tasks/LocalModUpdateTask.h" class ModDownloadTask : public SequentialTask { Q_OBJECT diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 615cfc0c..936b68d3 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -14,17 +14,19 @@ */ #include "ModFolderModel.h" + #include +#include +#include #include +#include +#include #include #include -#include -#include -#include -#include "ModFolderLoadTask.h" -#include #include -#include "LocalModParseTask.h" + +#include "minecraft/mod/tasks/LocalModParseTask.h" +#include "minecraft/mod/tasks/ModFolderLoadTask.h" ModFolderModel::ModFolderModel(const QString &dir) : QAbstractListModel(), m_dir(dir) { diff --git a/launcher/minecraft/mod/ModFolderModel.h b/launcher/minecraft/mod/ModFolderModel.h index f8ad4ca8..10a72691 100644 --- a/launcher/minecraft/mod/ModFolderModel.h +++ b/launcher/minecraft/mod/ModFolderModel.h @@ -24,8 +24,8 @@ #include "Mod.h" -#include "ModFolderLoadTask.h" -#include "LocalModParseTask.h" +#include "minecraft/mod/tasks/ModFolderLoadTask.h" +#include "minecraft/mod/tasks/LocalModParseTask.h" class LegacyInstance; class BaseInstance; diff --git a/launcher/minecraft/mod/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp similarity index 100% rename from launcher/minecraft/mod/LocalModParseTask.cpp rename to launcher/minecraft/mod/tasks/LocalModParseTask.cpp diff --git a/launcher/minecraft/mod/LocalModParseTask.h b/launcher/minecraft/mod/tasks/LocalModParseTask.h similarity index 90% rename from launcher/minecraft/mod/LocalModParseTask.h rename to launcher/minecraft/mod/tasks/LocalModParseTask.h index 0f119ba6..ed92394c 100644 --- a/launcher/minecraft/mod/LocalModParseTask.h +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.h @@ -1,9 +1,11 @@ #pragma once -#include + #include #include -#include "Mod.h" -#include "ModDetails.h" +#include + +#include "minecraft/mod/Mod.h" +#include "minecraft/mod/ModDetails.h" class LocalModParseTask : public QObject, public QRunnable { diff --git a/launcher/minecraft/mod/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp similarity index 100% rename from launcher/minecraft/mod/LocalModUpdateTask.cpp rename to launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp diff --git a/launcher/minecraft/mod/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h similarity index 100% rename from launcher/minecraft/mod/LocalModUpdateTask.h rename to launcher/minecraft/mod/tasks/LocalModUpdateTask.h diff --git a/launcher/minecraft/mod/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp similarity index 100% rename from launcher/minecraft/mod/ModFolderLoadTask.cpp rename to launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp diff --git a/launcher/minecraft/mod/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h similarity index 94% rename from launcher/minecraft/mod/ModFolderLoadTask.h rename to launcher/minecraft/mod/tasks/ModFolderLoadTask.h index c869f083..bb66022a 100644 --- a/launcher/minecraft/mod/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h @@ -1,10 +1,11 @@ #pragma once -#include -#include + #include #include -#include "Mod.h" +#include +#include #include +#include "minecraft/mod/Mod.h" class ModFolderLoadTask : public QObject, public QRunnable { From e9fb566c0797865a37e5b59a49163258b3adb328 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 15 Apr 2022 22:07:35 -0300 Subject: [PATCH 08/25] refactor: remove unused mod info and organize some stuff --- launcher/minecraft/mod/Mod.cpp | 87 ++++++++----------- launcher/minecraft/mod/Mod.h | 4 +- launcher/minecraft/mod/ModDetails.h | 15 +++- launcher/minecraft/mod/ModFolderModel.cpp | 10 +-- .../minecraft/mod/tasks/LocalModParseTask.cpp | 22 +---- .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 22 +++-- launcher/ui/widgets/MCModInfoFrame.cpp | 2 +- 7 files changed, 67 insertions(+), 95 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 59f4d83b..64c9ffb5 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -13,12 +13,13 @@ * limitations under the License. */ +#include "Mod.h" + #include #include -#include "Mod.h" -#include #include +#include namespace { @@ -26,8 +27,7 @@ ModDetails invalidDetails; } - -Mod::Mod(const QFileInfo &file) +Mod::Mod(const QFileInfo& file) { repath(file); m_changedDateTime = file.lastModified(); @@ -37,13 +37,12 @@ Mod::Mod(const QDir& mods_dir, const Packwiz::Mod& metadata) : m_file(mods_dir.absoluteFilePath(metadata.filename)) // It is weird, but name is not reliable for comparing with the JAR files name // FIXME: Maybe use hash when implemented? - , m_mmc_id(metadata.filename) + , m_internal_id(metadata.filename) , m_name(metadata.name) { - if(m_file.isDir()){ + if (m_file.isDir()) { m_type = MOD_FOLDER; - } - else{ + } else { if (metadata.filename.endsWith(".zip") || metadata.filename.endsWith(".jar")) m_type = MOD_ZIPFILE; else if (metadata.filename.endsWith(".litemod")) @@ -57,43 +56,32 @@ Mod::Mod(const QDir& mods_dir, const Packwiz::Mod& metadata) m_changedDateTime = m_file.lastModified(); } -void Mod::repath(const QFileInfo &file) +void Mod::repath(const QFileInfo& file) { m_file = file; QString name_base = file.fileName(); m_type = Mod::MOD_UNKNOWN; - m_mmc_id = name_base; + m_internal_id = name_base; - if (m_file.isDir()) - { + if (m_file.isDir()) { m_type = MOD_FOLDER; m_name = name_base; - } - else if (m_file.isFile()) - { - if (name_base.endsWith(".disabled")) - { + } else if (m_file.isFile()) { + if (name_base.endsWith(".disabled")) { m_enabled = false; name_base.chop(9); - } - else - { + } else { m_enabled = true; } - if (name_base.endsWith(".zip") || name_base.endsWith(".jar")) - { + if (name_base.endsWith(".zip") || name_base.endsWith(".jar")) { m_type = MOD_ZIPFILE; name_base.chop(4); - } - else if (name_base.endsWith(".litemod")) - { + } else if (name_base.endsWith(".litemod")) { m_type = MOD_LITEMOD; name_base.chop(8); - } - else - { + } else { m_type = MOD_SINGLEFILE; } m_name = name_base; @@ -109,23 +97,22 @@ bool Mod::enable(bool value) return false; QString path = m_file.absoluteFilePath(); - if (value) - { - QFile foo(path); + QFile file(path); + if (value) { if (!path.endsWith(".disabled")) return false; path.chop(9); - if (!foo.rename(path)) + + if (!file.rename(path)) return false; - } - else - { - QFile foo(path); + } else { path += ".disabled"; - if (!foo.rename(path)) + + if (!file.rename(path)) return false; } - if(!fromMetadata()) + + if (!fromMetadata()) repath(QFileInfo(path)); m_enabled = value; @@ -141,29 +128,25 @@ bool Mod::destroy(QDir& index_dir) return FS::deletePath(m_file.filePath()); } - -const ModDetails & Mod::details() const +const ModDetails& Mod::details() const { - if(!m_localDetails) - return invalidDetails; - return *m_localDetails; -} - - -QString Mod::version() const -{ - return details().version; + return m_localDetails ? *m_localDetails : invalidDetails; } QString Mod::name() const { - auto & d = details(); - if(!d.name.isEmpty()) { - return d.name; + auto d_name = details().name; + if (!d_name.isEmpty()) { + return d_name; } return m_name; } +QString Mod::version() const +{ + return details().version; +} + QString Mod::homeurl() const { return details().homeurl; diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index c9fd5813..46bb1a59 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -41,7 +41,7 @@ public: QFileInfo filename() const { return m_file; } QDateTime dateTimeChanged() const { return m_changedDateTime; } - QString mmc_id() const { return m_mmc_id; } + QString internal_id() const { return m_internal_id; } ModType type() const { return m_type; } bool fromMetadata() const { return m_from_metadata; } bool enabled() const { return m_enabled; } @@ -81,7 +81,7 @@ protected: QFileInfo m_file; QDateTime m_changedDateTime; - QString m_mmc_id; + QString m_internal_id; QString m_name; ModType m_type = MOD_UNKNOWN; bool m_from_metadata = false; diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index 6ab4aee7..d8d4f66f 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -5,13 +5,24 @@ struct ModDetails { + /* Mod ID as defined in the ModLoader-specific metadata */ QString mod_id; + + /* Human-readable name */ QString name; + + /* Human-readable mod version */ QString version; + + /* Human-readable minecraft version */ QString mcversion; + + /* URL for mod's home page */ QString homeurl; - QString updateurl; + + /* Human-readable description */ QString description; + + /* List of the author's names */ QStringList authors; - QString credits; }; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index 936b68d3..e2e041eb 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -159,7 +159,7 @@ void ModFolderModel::finishUpdate() modsIndex.clear(); int idx = 0; for(auto & mod: mods) { - modsIndex[mod.mmc_id()] = idx; + modsIndex[mod.internal_id()] = idx; idx++; } } @@ -182,7 +182,7 @@ void ModFolderModel::resolveMod(Mod& m) auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.filename()); auto result = task->result(); - result->id = m.mmc_id(); + result->id = m.internal_id(); activeTickets.insert(nextResolutionTicket, result); m.setResolving(true, nextResolutionTicket); nextResolutionTicket++; @@ -388,7 +388,7 @@ QVariant ModFolderModel::data(const QModelIndex &index, int role) const } case Qt::ToolTipRole: - return mods[row].mmc_id(); + return mods[row].internal_id(); case Qt::CheckStateRole: switch (column) @@ -443,11 +443,11 @@ bool ModFolderModel::setModStatus(int row, ModFolderModel::ModStatusAction actio } // preserve the row, but change its ID - auto oldId = mod.mmc_id(); + auto oldId = mod.internal_id(); if(!mod.enable(!mod.enabled())) { return false; } - auto newId = mod.mmc_id(); + auto newId = mod.internal_id(); if(modsIndex.contains(newId)) { // NOTE: this could handle a corner case, where we are overwriting a file, because the same 'mod' exists both enabled and disabled // But is it necessary? diff --git a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp index a7bec5ae..3354732b 100644 --- a/launcher/minecraft/mod/tasks/LocalModParseTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModParseTask.cpp @@ -35,7 +35,6 @@ std::shared_ptr ReadMCModInfo(QByteArray contents) details->name = name; } details->version = firstObj.value("version").toString(); - details->updateurl = firstObj.value("updateUrl").toString(); auto homeurl = firstObj.value("url").toString().trimmed(); if(!homeurl.isEmpty()) { @@ -57,7 +56,6 @@ std::shared_ptr ReadMCModInfo(QByteArray contents) { details->authors.append(author.toString()); } - details->credits = firstObj.value("credits").toString(); return details; }; QJsonParseError jsonError; @@ -168,27 +166,9 @@ std::shared_ptr ReadMCModTOML(QByteArray contents) } if(!authors.isEmpty()) { - // author information is stored as a string now, not a list details->authors.append(authors); } - // is credits even used anywhere? including this for completion/parity with old data version - toml_datum_t creditsDatum = toml_string_in(tomlData, "credits"); - QString credits = ""; - if(creditsDatum.ok) - { - authors = creditsDatum.u.s; - free(creditsDatum.u.s); - } - else - { - creditsDatum = toml_string_in(tomlModsTable0, "credits"); - if(creditsDatum.ok) - { - credits = creditsDatum.u.s; - free(creditsDatum.u.s); - } - } - details->credits = credits; + toml_datum_t homeurlDatum = toml_string_in(tomlData, "displayURL"); QString homeurl = ""; if(homeurlDatum.ok) diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index fd4d6008..bf7b28d6 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -3,32 +3,30 @@ #include "modplatform/packwiz/Packwiz.h" -ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) : - m_mods_dir(mods_dir), m_index_dir(index_dir), m_result(new Result()) -{ -} +ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) + : m_mods_dir(mods_dir), m_index_dir(index_dir), m_result(new Result()) +{} void ModFolderLoadTask::run() { // Read metadata first m_index_dir.refresh(); - for(auto entry : m_index_dir.entryList()){ + for (auto entry : m_index_dir.entryList()) { // QDir::Filter::NoDotAndDotDot seems to exclude all files for some reason... - if(entry == "." || entry == "..") + if (entry == "." || entry == "..") continue; - entry.chop(5); // Remove .toml at the end + entry.chop(5); // Remove .toml at the end Mod mod(m_mods_dir, Packwiz::getIndexForMod(m_index_dir, entry)); - m_result->mods[mod.mmc_id()] = mod; + m_result->mods[mod.internal_id()] = mod; } // Read JAR files that don't have metadata m_mods_dir.refresh(); - for (auto entry : m_mods_dir.entryInfoList()) - { + for (auto entry : m_mods_dir.entryInfoList()) { Mod mod(entry); - if(!m_result->mods.contains(mod.mmc_id())) - m_result->mods[mod.mmc_id()] = mod; + if (!m_result->mods.contains(mod.internal_id())) + m_result->mods[mod.internal_id()] = mod; } emit succeeded(); diff --git a/launcher/ui/widgets/MCModInfoFrame.cpp b/launcher/ui/widgets/MCModInfoFrame.cpp index 8c4bd690..7d78006b 100644 --- a/launcher/ui/widgets/MCModInfoFrame.cpp +++ b/launcher/ui/widgets/MCModInfoFrame.cpp @@ -32,7 +32,7 @@ void MCModInfoFrame::updateWithMod(Mod &m) QString text = ""; QString name = ""; if (m.name().isEmpty()) - name = m.mmc_id(); + name = m.internal_id(); else name = m.name(); From 092d2f8917271264871d69239ecb8836b34d0994 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 15 Apr 2022 22:37:10 -0300 Subject: [PATCH 09/25] feat: add support for converting builtin -> packwiz mod formats Also adds more documentation. --- launcher/modplatform/packwiz/Packwiz.cpp | 41 ++++++++++++++++++++---- launcher/modplatform/packwiz/Packwiz.h | 36 +++++++++++++-------- 2 files changed, 58 insertions(+), 19 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index bfadf7cb..445d64fb 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -1,12 +1,14 @@ #include "Packwiz.h" -#include "modplatform/ModIndex.h" -#include "toml.h" - #include #include #include +#include "toml.h" + +#include "modplatform/ModIndex.h" +#include "minecraft/mod/Mod.h" + // Helpers static inline QString indexFileName(QString const& mod_name) { @@ -31,12 +33,39 @@ auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pac return mod; } +auto Packwiz::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod +{ + auto mod_name = internal_mod.name(); + + // Try getting metadata if it exists + Mod mod { getIndexForMod(index_dir, mod_name) }; + if(mod.isValid()) + return mod; + + // Manually construct packwiz mod + mod.name = internal_mod.name(); + mod.filename = internal_mod.filename().fileName(); + + // TODO: Have a mechanism for telling the UI subsystem that we want to gather user information + // (i.e. which mod provider we want to use). Maybe an object parameter with a signal for that? + + return mod; +} + void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) { + if(!mod.isValid()){ + qCritical() << QString("Tried to update metadata of an invalid mod!"); + return; + } + // Ensure the corresponding mod's info exists, and create it if not QFile index_file(index_dir.absoluteFilePath(indexFileName(mod.name))); // There's already data on there! + // TODO: We should do more stuff here, as the user is likely trying to + // override a file. In this case, check versions and ask the user what + // they want to do! if (index_file.exists()) { index_file.remove(); } if (!index_file.open(QIODevice::ReadWrite)) { @@ -87,11 +116,11 @@ auto Packwiz::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod if (!index_file.exists()) { qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(mod_name); - return mod; + return {}; } if (!index_file.open(QIODevice::ReadOnly)) { qWarning() << QString("Failed to open mod metadata for %1").arg(mod_name); - return mod; + return {}; } toml_table_t* table; @@ -103,7 +132,7 @@ auto Packwiz::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod if (!table) { qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); - return mod; + return {}; } // Helper function for extracting data from the TOML file diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 541059d0..457d268a 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -6,33 +6,43 @@ #include #include -namespace ModPlatform { -} // namespace ModPlatform - class QDir; +// Mod from launcher/minecraft/mod/Mod.h +class Mod; + class Packwiz { public: struct Mod { - QString name; - QString filename; + QString name {}; + QString filename {}; // FIXME: make side an enum - QString side = "both"; + QString side {"both"}; // [download] - QUrl url; + QUrl url {}; // FIXME: make hash-format an enum - QString hash_format; - QString hash; + QString hash_format {}; + QString hash {}; // [update] - ModPlatform::Provider provider; - QVariant file_id; - QVariant project_id; + ModPlatform::Provider provider {}; + QVariant file_id {}; + QVariant project_id {}; + + public: + // This is a heuristic, but should work for now. + auto isValid() const -> bool { return !name.isEmpty(); } }; - /* Generates the object representing the information in a mod.toml file via its common representation in the launcher */ + /* Generates the object representing the information in a mod.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; + /* Generates the object representing the information in a mod.toml file via + * its common representation in the launcher. + * */ + static auto createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod; /* Updates the mod index for the provided mod. * This creates a new index if one does not exist already From fab4a7a6029beb60bade312ee89e649202d178de Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 16 Apr 2022 13:27:29 -0300 Subject: [PATCH 10/25] refactor: abstract metadata handling and clarify names --- launcher/CMakeLists.txt | 1 + launcher/MMCZip.cpp | 14 +++---- launcher/minecraft/MinecraftInstance.cpp | 10 ++--- launcher/minecraft/mod/MetadataHandler.h | 41 +++++++++++++++++++ launcher/minecraft/mod/Mod.cpp | 6 +-- launcher/minecraft/mod/Mod.h | 7 ++-- launcher/minecraft/mod/ModFolderModel.cpp | 2 +- .../mod/tasks/LocalModUpdateTask.cpp | 6 +-- .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 4 +- launcher/modplatform/packwiz/Packwiz.cpp | 16 +++++--- launcher/modplatform/packwiz/Packwiz.h | 6 ++- 11 files changed, 82 insertions(+), 31 deletions(-) create mode 100644 launcher/minecraft/mod/MetadataHandler.h diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b6df2851..03d68e66 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -322,6 +322,7 @@ set(MINECRAFT_SOURCES minecraft/WorldList.h minecraft/WorldList.cpp + minecraft/mod/MetadataHandler.h minecraft/mod/Mod.h minecraft/mod/Mod.cpp minecraft/mod/ModDetails.h diff --git a/launcher/MMCZip.cpp b/launcher/MMCZip.cpp index 8591fcc0..627ceaf1 100644 --- a/launcher/MMCZip.cpp +++ b/launcher/MMCZip.cpp @@ -151,23 +151,23 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const continue; if (mod.type() == Mod::MOD_ZIPFILE) { - if (!mergeZipFiles(&zipOut, mod.filename(), addedFiles)) + if (!mergeZipFiles(&zipOut, mod.fileinfo(), addedFiles)) { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; return false; } } else if (mod.type() == Mod::MOD_SINGLEFILE) { // FIXME: buggy - does not work with addedFiles - auto filename = mod.filename(); + auto filename = mod.fileinfo(); if (!JlCompress::compressFile(&zipOut, filename.absoluteFilePath(), filename.fileName())) { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; return false; } addedFiles.insert(filename.fileName()); @@ -176,7 +176,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const { // untested, but seems to be unused / not possible to reach // FIXME: buggy - does not work with addedFiles - auto filename = mod.filename(); + auto filename = mod.fileinfo(); QString what_to_zip = filename.absoluteFilePath(); QDir dir(what_to_zip); dir.cdUp(); @@ -193,7 +193,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const { zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add" << mod.filename().fileName() << "to the jar."; + qCritical() << "Failed to add" << mod.fileinfo().fileName() << "to the jar."; return false; } qDebug() << "Adding folder " << filename.fileName() << " from " @@ -204,7 +204,7 @@ bool MMCZip::createModdedJar(QString sourceJarPath, QString targetJarPath, const // Make sure we do not continue launching when something is missing or undefined... zipOut.close(); QFile::remove(targetJarPath); - qCritical() << "Failed to add unknown mod type" << mod.filename().fileName() << "to the jar."; + qCritical() << "Failed to add unknown mod type" << mod.fileinfo().fileName() << "to the jar."; return false; } } diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 61326fac..2f339014 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -659,23 +659,23 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << QString("%1:").arg(label); auto modList = model.allMods(); std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) { - auto aName = a.filename().completeBaseName(); - auto bName = b.filename().completeBaseName(); + auto aName = a.fileinfo().completeBaseName(); + auto bName = b.fileinfo().completeBaseName(); return aName.localeAwareCompare(bName) < 0; }); for(auto & mod: modList) { if(mod.type() == Mod::MOD_FOLDER) { - out << u8" [📁] " + mod.filename().completeBaseName() + " (folder)"; + out << u8" [📁] " + mod.fileinfo().completeBaseName() + " (folder)"; continue; } if(mod.enabled()) { - out << u8" [✔️] " + mod.filename().completeBaseName(); + out << u8" [✔️] " + mod.fileinfo().completeBaseName(); } else { - out << u8" [❌] " + mod.filename().completeBaseName() + " (disabled)"; + out << u8" [❌] " + mod.fileinfo().completeBaseName() + " (disabled)"; } } diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h new file mode 100644 index 00000000..26b1f799 --- /dev/null +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#include "modplatform/packwiz/Packwiz.h" + +// 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; + + 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); + } + + static auto create(QDir& index_dir, Mod& internal_mod) -> ModStruct + { + return Packwiz::V1::createModFormat(index_dir, internal_mod); + } + + static void update(QDir& index_dir, ModStruct& mod) + { + Packwiz::V1::updateModIndex(index_dir, mod); + } + + static void remove(QDir& index_dir, QString& mod_name) + { + Packwiz::V1::deleteModIndex(index_dir, mod_name); + } + + static auto get(QDir& index_dir, QString& mod_name) -> ModStruct + { + return Packwiz::V1::getIndexForMod(index_dir, mod_name); + } +}; diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 64c9ffb5..5b35156d 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -20,6 +20,7 @@ #include #include +#include "MetadataHandler.h" namespace { @@ -33,7 +34,7 @@ Mod::Mod(const QFileInfo& file) m_changedDateTime = file.lastModified(); } -Mod::Mod(const QDir& mods_dir, const Packwiz::Mod& metadata) +Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) : m_file(mods_dir.absoluteFilePath(metadata.filename)) // It is weird, but name is not reliable for comparing with the JAR files name // FIXME: Maybe use hash when implemented? @@ -121,8 +122,7 @@ bool Mod::enable(bool value) bool Mod::destroy(QDir& index_dir) { - // Delete metadata - Packwiz::deleteModIndex(index_dir, m_name); + Metadata::remove(index_dir, m_name); m_type = MOD_UNKNOWN; return FS::deletePath(m_file.filePath()); diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 46bb1a59..fef8cbe4 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -21,7 +21,7 @@ #include #include "ModDetails.h" -#include "modplatform/packwiz/Packwiz.h" +#include "minecraft/mod/MetadataHandler.h" class Mod { @@ -37,9 +37,9 @@ public: Mod() = default; Mod(const QFileInfo &file); - explicit Mod(const QDir& mods_dir, const Packwiz::Mod& metadata); + explicit Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata); - QFileInfo filename() const { return m_file; } + QFileInfo fileinfo() const { return m_file; } QDateTime dateTimeChanged() const { return m_changedDateTime; } QString internal_id() const { return m_internal_id; } ModType type() const { return m_type; } @@ -82,6 +82,7 @@ protected: QDateTime m_changedDateTime; QString m_internal_id; + /* Name as reported via the file name */ QString m_name; ModType m_type = MOD_UNKNOWN; bool m_from_metadata = false; diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index e2e041eb..e034e35e 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -180,7 +180,7 @@ void ModFolderModel::resolveMod(Mod& m) return; } - auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.filename()); + auto task = new LocalModParseTask(nextResolutionTicket, m.type(), m.fileinfo()); auto result = task->result(); result->id = m.internal_id(); activeTickets.insert(nextResolutionTicket, result); diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index 63f5cf9a..8b6e8ec7 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -3,7 +3,7 @@ #include #include "FileSystem.h" -#include "modplatform/packwiz/Packwiz.h" +#include "minecraft/mod/MetadataHandler.h" 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) @@ -18,8 +18,8 @@ void LocalModUpdateTask::executeTask() { setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name)); - auto pw_mod = Packwiz::createModFormat(m_index_dir, m_mod, m_mod_version); - Packwiz::updateModIndex(m_index_dir, pw_mod); + auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version); + Metadata::update(m_index_dir, pw_mod); emitSucceeded(); } diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index bf7b28d6..e94bdee9 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -1,7 +1,7 @@ #include "ModFolderLoadTask.h" #include -#include "modplatform/packwiz/Packwiz.h" +#include "minecraft/mod/MetadataHandler.h" ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) : m_mods_dir(mods_dir), m_index_dir(index_dir), m_result(new Result()) @@ -17,7 +17,7 @@ void ModFolderLoadTask::run() continue; entry.chop(5); // Remove .toml at the end - Mod mod(m_mods_dir, Packwiz::getIndexForMod(m_index_dir, entry)); + Mod mod(m_mods_dir, Metadata::get(m_index_dir, entry)); m_result->mods[mod.internal_id()] = mod; } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 445d64fb..27339c2d 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -9,13 +9,15 @@ #include "modplatform/ModIndex.h" #include "minecraft/mod/Mod.h" +namespace Packwiz { + // Helpers static inline QString indexFileName(QString const& mod_name) { return QString("%1.toml").arg(mod_name); } -auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod +auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -33,7 +35,7 @@ auto Packwiz::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pac return mod; } -auto Packwiz::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod +auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod { auto mod_name = internal_mod.name(); @@ -44,7 +46,7 @@ auto Packwiz::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod // Manually construct packwiz mod mod.name = internal_mod.name(); - mod.filename = internal_mod.filename().fileName(); + mod.filename = internal_mod.fileinfo().fileName(); // TODO: Have a mechanism for telling the UI subsystem that we want to gather user information // (i.e. which mod provider we want to use). Maybe an object parameter with a signal for that? @@ -52,7 +54,7 @@ auto Packwiz::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod return mod; } -void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) +void V1::updateModIndex(QDir& index_dir, Mod& mod) { if(!mod.isValid()){ qCritical() << QString("Tried to update metadata of an invalid mod!"); @@ -94,7 +96,7 @@ void Packwiz::updateModIndex(QDir& index_dir, Mod& mod) } } -void Packwiz::deleteModIndex(QDir& index_dir, QString& mod_name) +void V1::deleteModIndex(QDir& index_dir, QString& mod_name) { QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name))); @@ -108,7 +110,7 @@ void Packwiz::deleteModIndex(QDir& index_dir, QString& mod_name) } } -auto Packwiz::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod +auto V1::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod { Mod mod; @@ -201,3 +203,5 @@ auto Packwiz::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod return mod; } + +} // namespace Packwiz diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 457d268a..777a365f 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -11,7 +11,9 @@ class QDir; // Mod from launcher/minecraft/mod/Mod.h class Mod; -class Packwiz { +namespace Packwiz { + +class V1 { public: struct Mod { QString name {}; @@ -58,3 +60,5 @@ class Packwiz { * */ static auto getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod; }; + +} // namespace Packwiz From 23febc6d94bcc5903a9863ba7b854b5091b0813b Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 17 Apr 2022 09:30:32 -0300 Subject: [PATCH 11/25] feat: cache metadata in ModDetails Allows for more easy access to the metadata by outside entities --- launcher/minecraft/mod/Mod.cpp | 14 ++++++++++++++ launcher/minecraft/mod/Mod.h | 14 ++++++++------ launcher/minecraft/mod/ModDetails.h | 7 +++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 5b35156d..46776239 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -55,6 +55,8 @@ Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) m_from_metadata = true; m_enabled = true; m_changedDateTime = m_file.lastModified(); + + m_temp_metadata = std::make_shared(std::move(metadata)); } void Mod::repath(const QFileInfo& file) @@ -161,3 +163,15 @@ QStringList Mod::authors() const { return details().authors; } + +void Mod::finishResolvingWithDetails(std::shared_ptr details) +{ + m_resolving = false; + m_resolved = true; + m_localDetails = details; + + if (fromMetadata() && m_temp_metadata->isValid()) { + m_localDetails->metadata = m_temp_metadata; + m_temp_metadata.reset(); + } +} diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index fef8cbe4..0d49d94b 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -18,7 +18,6 @@ #include #include #include -#include #include "ModDetails.h" #include "minecraft/mod/MetadataHandler.h" @@ -55,6 +54,9 @@ public: QString description() const; QStringList authors() const; + const std::shared_ptr metadata() const { return details().metadata; }; + std::shared_ptr metadata() { return m_localDetails->metadata; }; + bool enable(bool value); // delete all the files of this mod @@ -71,11 +73,7 @@ public: m_resolving = resolving; m_resolutionTicket = resolutionTicket; } - void finishResolvingWithDetails(std::shared_ptr details){ - m_resolving = false; - m_resolved = true; - m_localDetails = details; - } + void finishResolvingWithDetails(std::shared_ptr details); protected: QFileInfo m_file; @@ -86,6 +84,10 @@ protected: QString m_name; ModType m_type = MOD_UNKNOWN; bool m_from_metadata = false; + + /* If the mod has metadata, this will be filled in the constructor, and passed to + * the ModDetails when calling finishResolvingWithDetails */ + std::shared_ptr m_temp_metadata; std::shared_ptr m_localDetails; bool m_enabled = true; diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index d8d4f66f..f9973fc2 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -1,8 +1,12 @@ #pragma once +#include + #include #include +#include "minecraft/mod/MetadataHandler.h" + struct ModDetails { /* Mod ID as defined in the ModLoader-specific metadata */ @@ -25,4 +29,7 @@ struct ModDetails /* List of the author's names */ QStringList authors; + + /* Metadata information, if any */ + std::shared_ptr metadata; }; From 4439666e67573a6a36af981fdc68410fdf9e4f9f Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 17 Apr 2022 10:19:23 -0300 Subject: [PATCH 12/25] feat: allow disabling mod metadata usage --- launcher/Application.cpp | 3 + launcher/minecraft/mod/Mod.cpp | 6 +- .../mod/tasks/LocalModUpdateTask.cpp | 6 ++ .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 28 +++--- .../minecraft/mod/tasks/ModFolderLoadTask.h | 4 + launcher/ui/pages/global/LauncherPage.cpp | 12 +++ launcher/ui/pages/global/LauncherPage.h | 1 + launcher/ui/pages/global/LauncherPage.ui | 87 ++++++++++++------- 8 files changed, 107 insertions(+), 40 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ba4096b6..ae4cbcf8 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -643,6 +643,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) // Minecraft launch method m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); + // Minecraft mods + m_settings->registerSetting("DontUseModMetadata", false); + // Minecraft offline player name m_settings->registerSetting("LastOfflinePlayerName", ""); diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 46776239..7b560845 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -124,7 +124,11 @@ bool Mod::enable(bool value) bool Mod::destroy(QDir& index_dir) { - Metadata::remove(index_dir, m_name); + auto n = name(); + // FIXME: This can fail to remove the metadata if the + // "DontUseModMetadata" setting is on, since there could + // be a name mismatch! + Metadata::remove(index_dir, n); m_type = MOD_UNKNOWN; return FS::deletePath(m_file.filePath()); diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index 8b6e8ec7..3c9b76a8 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -2,6 +2,7 @@ #include +#include "Application.h" #include "FileSystem.h" #include "minecraft/mod/MetadataHandler.h" @@ -18,6 +19,11 @@ void LocalModUpdateTask::executeTask() { setStatus(tr("Updating index for mod:\n%1").arg(m_mod.name)); + if(APPLICATION->settings()->get("DontUseModMetadata").toBool()){ + emitSucceeded(); + return; + } + auto pw_mod = Metadata::create(m_index_dir, m_mod, m_mod_version); Metadata::update(m_index_dir, pw_mod); diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index e94bdee9..5afbb08a 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -1,6 +1,7 @@ #include "ModFolderLoadTask.h" #include +#include "Application.h" #include "minecraft/mod/MetadataHandler.h" ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) @@ -9,16 +10,9 @@ ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) void ModFolderLoadTask::run() { - // Read metadata first - m_index_dir.refresh(); - for (auto entry : m_index_dir.entryList()) { - // QDir::Filter::NoDotAndDotDot seems to exclude all files for some reason... - if (entry == "." || entry == "..") - continue; - - entry.chop(5); // Remove .toml at the end - Mod mod(m_mods_dir, Metadata::get(m_index_dir, entry)); - m_result->mods[mod.internal_id()] = mod; + if (!APPLICATION->settings()->get("DontUseModMetadata").toBool()) { + // Read metadata first + getFromMetadata(); } // Read JAR files that don't have metadata @@ -31,3 +25,17 @@ void ModFolderLoadTask::run() emit succeeded(); } + +void ModFolderLoadTask::getFromMetadata() +{ + m_index_dir.refresh(); + for (auto entry : m_index_dir.entryList()) { + // QDir::Filter::NoDotAndDotDot seems to exclude all files for some reason... + if (entry == "." || entry == "..") + continue; + + entry.chop(5); // Remove .toml at the end + Mod mod(m_mods_dir, Metadata::get(m_index_dir, entry)); + m_result->mods[mod.internal_id()] = mod; + } +} diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h index bb66022a..ba997874 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h @@ -24,6 +24,10 @@ public: void run(); signals: void succeeded(); + +private: + void getFromMetadata(); + private: QDir& m_mods_dir, m_index_dir; ResultPtr m_result; diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index af2e2cd1..8754c0ec 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -184,6 +184,11 @@ void LauncherPage::on_modsDirBrowseBtn_clicked() } } +void LauncherPage::on_metadataDisableBtn_clicked() +{ + ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); +} + void LauncherPage::refreshUpdateChannelList() { // Stop listening for selection changes. It's going to change a lot while we update it and @@ -338,6 +343,9 @@ void LauncherPage::applySettings() s->set("InstSortMode", "Name"); break; } + + // Mods + s->set("DontUseModMetadata", ui->metadataDisableBtn->isChecked()); } void LauncherPage::loadSettings() { @@ -440,6 +448,10 @@ void LauncherPage::loadSettings() { ui->sortByNameBtn->setChecked(true); } + + // Mods + ui->metadataDisableBtn->setChecked(s->get("DontUseModMetadata").toBool()); + ui->metadataWarningLabel->setHidden(!ui->metadataDisableBtn->isChecked()); } void LauncherPage::refreshFontPreview() diff --git a/launcher/ui/pages/global/LauncherPage.h b/launcher/ui/pages/global/LauncherPage.h index bbf5d2fe..f38c922e 100644 --- a/launcher/ui/pages/global/LauncherPage.h +++ b/launcher/ui/pages/global/LauncherPage.h @@ -88,6 +88,7 @@ slots: void on_instDirBrowseBtn_clicked(); void on_modsDirBrowseBtn_clicked(); void on_iconsDirBrowseBtn_clicked(); + void on_metadataDisableBtn_clicked(); /*! * Updates the list of update channels in the combo box. diff --git a/launcher/ui/pages/global/LauncherPage.ui b/launcher/ui/pages/global/LauncherPage.ui index ae7eb73f..417bbe05 100644 --- a/launcher/ui/pages/global/LauncherPage.ui +++ b/launcher/ui/pages/global/LauncherPage.ui @@ -94,19 +94,13 @@ Folders - - + + - I&nstances: - - - instDirTextBox + ... - - - @@ -114,28 +108,15 @@ - - - - &Mods: - - - modsDirTextBox - - - - - - - - + + ... - - + + @@ -147,10 +128,58 @@ - - + + + + + - ... + I&nstances: + + + instDirTextBox + + + + + + + + + + &Mods: + + + modsDirTextBox + + + + + + + + + + Mods + + + + + + Disable using metadata provided by mod providers (like Modrinth or Curseforge) for mods. + + + Disable using metadata for mods? + + + + + + + <html><head/><body><p><span style=" font-weight:600; color:#f5c211;">Warning</span><span style=" color:#f5c211;">: Disabling mod metadata may also disable some upcoming QoL features, such as mod updating!</span></p></body></html> + + + true From d7f6b3699074b268fd554bd1eb9da68f1e533355 Mon Sep 17 00:00:00 2001 From: flow Date: Sun, 17 Apr 2022 11:40:41 -0300 Subject: [PATCH 13/25] test+fix: add basic tests and fix issues with it --- launcher/CMakeLists.txt | 6 ++ launcher/minecraft/mod/Mod.cpp | 7 +- launcher/minecraft/mod/Mod.h | 1 - .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 10 ++- launcher/modplatform/ModIndex.h | 2 +- launcher/modplatform/packwiz/Packwiz.cpp | 76 +++++++++++------- launcher/modplatform/packwiz/Packwiz.h | 11 ++- launcher/modplatform/packwiz/Packwiz_test.cpp | 68 ++++++++++++++++ .../packwiz/testdata/borderless-mining.toml | Bin 0 -> 431 bytes .../screenshot-to-clipboard-fabric.toml | Bin 0 -> 323 bytes 10 files changed, 142 insertions(+), 39 deletions(-) create mode 100644 launcher/modplatform/packwiz/Packwiz_test.cpp create mode 100644 launcher/modplatform/packwiz/testdata/borderless-mining.toml create mode 100644 launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.toml diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 03d68e66..6c7b5e43 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -551,6 +551,12 @@ set(PACKWIZ_SOURCES modplatform/packwiz/Packwiz.cpp ) +add_unit_test(Packwiz + SOURCES modplatform/packwiz/Packwiz_test.cpp + DATA modplatform/packwiz/testdata + LIBS Launcher_logic + ) + set(TECHNIC_SOURCES modplatform/technic/SingleZipPackInstallTask.h modplatform/technic/SingleZipPackInstallTask.cpp diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 7b560845..ef3699e8 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -20,6 +20,8 @@ #include #include + +#include "Application.h" #include "MetadataHandler.h" namespace { @@ -174,8 +176,7 @@ void Mod::finishResolvingWithDetails(std::shared_ptr details) m_resolved = true; m_localDetails = details; - if (fromMetadata() && m_temp_metadata->isValid()) { - m_localDetails->metadata = m_temp_metadata; - m_temp_metadata.reset(); + if (fromMetadata() && m_temp_metadata->isValid() && m_localDetails.get()) { + m_localDetails->metadata.swap(m_temp_metadata); } } diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 0d49d94b..1e7ed1ed 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -20,7 +20,6 @@ #include #include "ModDetails.h" -#include "minecraft/mod/MetadataHandler.h" class Mod { diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 5afbb08a..03a17461 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -34,8 +34,14 @@ void ModFolderLoadTask::getFromMetadata() if (entry == "." || entry == "..") continue; - entry.chop(5); // Remove .toml at the end - Mod mod(m_mods_dir, Metadata::get(m_index_dir, entry)); + auto metadata = Metadata::get(m_index_dir, entry); + // TODO: Don't simply return. Instead, show to the user that the metadata is there, but + // it's not currently 'installed' (i.e. there's no JAR file yet). + if(!metadata.isValid()){ + return; + } + + Mod mod(m_mods_dir, metadata); m_result->mods[mod.internal_id()] = mod; } } diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index c5329772..ee623b78 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -19,7 +19,7 @@ class ProviderCapabilities { { switch(p){ case Provider::MODRINTH: - return "sha256"; + return "sha512"; case Provider::FLAME: return "murmur2"; } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 27339c2d..8fd74a3e 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -14,6 +14,8 @@ namespace Packwiz { // Helpers static inline QString indexFileName(QString const& mod_name) { + if(mod_name.endsWith(".toml")) + return mod_name; return QString("%1.toml").arg(mod_name); } @@ -91,8 +93,16 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) in_stream << QString("\n[update]\n"); in_stream << QString("[update.%1]\n").arg(ModPlatform::ProviderCapabilities::providerName(mod.provider)); - addToStream("file-id", mod.file_id.toString()); - addToStream("project-id", mod.project_id.toString()); + switch(mod.provider){ + case(ModPlatform::Provider::FLAME): + in_stream << QString("file-id = %1\n").arg(mod.file_id.toString()); + in_stream << QString("project-id = %1\n").arg(mod.project_id.toString()); + break; + case(ModPlatform::Provider::MODRINTH): + addToStream("mod-id", mod.mod_id().toString()); + addToStream("version", mod.version().toString()); + break; + } } } @@ -110,18 +120,44 @@ void V1::deleteModIndex(QDir& index_dir, QString& mod_name) } } -auto V1::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod +// Helper functions for extracting data from the TOML file +static auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString +{ + toml_datum_t var = toml_string_in(parent, entry_name); + if (!var.ok) { + qCritical() << QString("Failed to read str property '%1' in mod metadata.").arg(entry_name); + return {}; + } + + QString tmp = var.u.s; + free(var.u.s); + + return tmp; +} + +static auto intEntry(toml_table_t* parent, const char* entry_name) -> int +{ + toml_datum_t var = toml_int_in(parent, entry_name); + if (!var.ok) { + qCritical() << QString("Failed to read int property '%1' in mod metadata.").arg(entry_name); + return {}; + } + + return var.u.i; +} + +auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod { Mod mod; - QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name))); + QFile index_file(index_dir.absoluteFilePath(indexFileName(index_file_name))); if (!index_file.exists()) { - qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(mod_name); + qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(index_file_name); return {}; } if (!index_file.open(QIODevice::ReadOnly)) { - qWarning() << QString("Failed to open mod metadata for %1").arg(mod_name); + qWarning() << QString("Failed to open mod metadata for %1").arg(index_file_name); return {}; } @@ -136,29 +172,9 @@ auto V1::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); return {}; } - - // Helper function for extracting data from the TOML file - auto stringEntry = [&](toml_table_t* parent, const char* entry_name) -> QString { - toml_datum_t var = toml_string_in(parent, entry_name); - if (!var.ok) { - qCritical() << QString("Failed to read property '%1' in mod metadata.").arg(entry_name); - return {}; - } - - QString tmp = var.u.s; - free(var.u.s); - - return tmp; - }; - + { // Basic info mod.name = stringEntry(table, "name"); - // Basic sanity check - if (mod.name != mod_name) { - qCritical() << QString("Name mismatch in mod metadata:\nExpected:%1\nGot:%2").arg(mod_name, mod.name); - return {}; - } - mod.filename = stringEntry(table, "filename"); mod.side = stringEntry(table, "side"); } @@ -188,15 +204,17 @@ auto V1::getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod toml_table_t* mod_provider_table; if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::FLAME)))) { 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 = toml_table_in(update_table, ProviderCaps::providerName(Provider::MODRINTH)))) { mod.provider = Provider::MODRINTH; + mod.mod_id() = stringEntry(mod_provider_table, "mod-id"); + mod.version() = stringEntry(mod_provider_table, "version"); } else { qCritical() << QString("No mod provider on mod metadata!"); return {}; } - mod.file_id = stringEntry(mod_provider_table, "file-id"); - mod.project_id = stringEntry(mod_provider_table, "project-id"); } toml_free(table); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 777a365f..69125dbc 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -33,8 +33,13 @@ class V1 { QVariant project_id {}; public: - // This is a heuristic, but should work for now. - auto isValid() const -> bool { return !name.isEmpty(); } + // This is a totally heuristic, but should work for now. + auto isValid() const -> bool { return !name.isEmpty() && !project_id.isNull(); } + + // Different providers can use different names for the same thing + // Modrinth-specific + auto mod_id() -> QVariant& { return project_id; } + auto version() -> QVariant& { return file_id; } }; /* Generates the object representing the information in a mod.toml file via @@ -58,7 +63,7 @@ class V1 { /* Gets the metadata for a mod with a particular name. * If the mod doesn't have a metadata, it simply returns an empty Mod object. * */ - static auto getIndexForMod(QDir& index_dir, QString& mod_name) -> Mod; + static auto getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod; }; } // namespace Packwiz diff --git a/launcher/modplatform/packwiz/Packwiz_test.cpp b/launcher/modplatform/packwiz/Packwiz_test.cpp new file mode 100644 index 00000000..2e61c167 --- /dev/null +++ b/launcher/modplatform/packwiz/Packwiz_test.cpp @@ -0,0 +1,68 @@ +#include +#include + +#include "TestUtil.h" +#include "Packwiz.h" + +class PackwizTest : public QObject { + Q_OBJECT + + private slots: + // Files taken from https://github.com/packwiz/packwiz-example-pack + void loadFromFile_Modrinth() + { + QString source = QFINDTESTDATA("testdata"); + + QDir index_dir(source); + QString name_mod("borderless-mining.toml"); + QVERIFY(index_dir.entryList().contains(name_mod)); + + auto metadata = Packwiz::V1::getIndexForMod(index_dir, name_mod); + + QVERIFY(metadata.isValid()); + + QCOMPARE(metadata.name, "Borderless Mining"); + QCOMPARE(metadata.filename, "borderless-mining-1.1.1+1.18.jar"); + QCOMPARE(metadata.side, "client"); + + QCOMPARE(metadata.url, QUrl("https://cdn.modrinth.com/data/kYq5qkSL/versions/1.1.1+1.18/borderless-mining-1.1.1+1.18.jar")); + QCOMPARE(metadata.hash_format, "sha512"); + QCOMPARE(metadata.hash, "c8fe6e15ddea32668822dddb26e1851e5f03834be4bcb2eff9c0da7fdc086a9b6cead78e31a44d3bc66335cba11144ee0337c6d5346f1ba63623064499b3188d"); + + QCOMPARE(metadata.provider, ModPlatform::Provider::MODRINTH); + QCOMPARE(metadata.version(), "ug2qKTPR"); + QCOMPARE(metadata.mod_id(), "kYq5qkSL"); + } + + void loadFromFile_Curseforge() + { + QString source = QFINDTESTDATA("testdata"); + + QDir index_dir(source); + QString name_mod("screenshot-to-clipboard-fabric.toml"); + QVERIFY(index_dir.entryList().contains(name_mod)); + + // Try without the .toml at the end + name_mod.chop(5); + + auto metadata = Packwiz::V1::getIndexForMod(index_dir, name_mod); + + QVERIFY(metadata.isValid()); + + QCOMPARE(metadata.name, "Screenshot to Clipboard (Fabric)"); + QCOMPARE(metadata.filename, "screenshot-to-clipboard-1.0.7-fabric.jar"); + QCOMPARE(metadata.side, "both"); + + QCOMPARE(metadata.url, QUrl("https://edge.forgecdn.net/files/3509/43/screenshot-to-clipboard-1.0.7-fabric.jar")); + QCOMPARE(metadata.hash_format, "murmur2"); + QCOMPARE(metadata.hash, "1781245820"); + + QCOMPARE(metadata.provider, ModPlatform::Provider::FLAME); + QCOMPARE(metadata.file_id, 3509043); + QCOMPARE(metadata.project_id, 327154); + } +}; + +QTEST_GUILESS_MAIN(PackwizTest) + +#include "Packwiz_test.moc" diff --git a/launcher/modplatform/packwiz/testdata/borderless-mining.toml b/launcher/modplatform/packwiz/testdata/borderless-mining.toml new file mode 100644 index 0000000000000000000000000000000000000000..16545fd43676d21c043ae9600d9a17eb68101652 GIT binary patch literal 431 zcmaiw%TB{E5JmU?iYi-_haYjBN^IBy5&|{|buspg8`H#T;}m|Mq-x6&StEJwmFCWz z2tBRtSJ}fbB8?rTw0aIP#9hXG=qO%nd$aTYZ0Ed~-`!lM_<}KGDd2gK>jK3oW9$=$ zpV$q6TXq_|C8M3DL)w(3!&vkKjv-EM;fB6Mn4sK$9P8u$?Wz2xF@+(f@-L$NKfi_4 z=6)D^n3k;6Ld`|S7J2EN@uZ2@hy+q-ZHy3zXvHj=np5p7X{55Gth0i=Z(N12_UJ03 zp|RQ#;M$PnpcG2$w3f1V7C7fh5mi#IoyJ-!?YRXlwUCuos%fm`#^3_vbeIpN?e%kG cuw^riJm9kDl|sfY7#8ug6UWE*m)DH_0+y+Si~s-t literal 0 HcmV?d00001 diff --git a/launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.toml b/launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.toml new file mode 100644 index 0000000000000000000000000000000000000000..87d70ada52ca3e8b709d08a75c4ccbc15222c2ac GIT binary patch literal 323 zcma)%y>7%H5QKZ40#PLe;9vPJQmROKfs~O84C{lF&04TlUO&dVOC4#8)o5nF*=Sba z?_7M@1Q4@F;)MKT3EPAwIsWo#rWEX}U~^a?KHT}wEeWN4x@D~@HOTplsJlsm<>1cy z6OtE{ePr4*~{b7YN!C# zJsr~sR`ep&!=-Mz{?b&X(7riCFg_P$_mtu6F`h5W;EqsfQFSfb65hemLu`h+@7OQN C250L4 literal 0 HcmV?d00001 From ba50765c306d2907e411bc0ed9a10d990cf42fd3 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 19 Apr 2022 20:19:51 -0300 Subject: [PATCH 14/25] tidy: apply clang-tidy to some files Mostly the ones created in this PR + Mod.h / Mod.cpp / ModDetails.h --- launcher/minecraft/mod/Mod.cpp | 16 ++++---- launcher/minecraft/mod/Mod.h | 40 +++++++++---------- .../mod/tasks/LocalModUpdateTask.cpp | 2 +- .../minecraft/mod/tasks/LocalModUpdateTask.h | 6 +-- launcher/modplatform/packwiz/Packwiz.cpp | 9 +++-- launcher/modplatform/packwiz/Packwiz_test.cpp | 2 +- 6 files changed, 38 insertions(+), 37 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index ef3699e8..992b91dc 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -93,7 +93,7 @@ void Mod::repath(const QFileInfo& file) } } -bool Mod::enable(bool value) +auto Mod::enable(bool value) -> bool { if (m_type == Mod::MOD_UNKNOWN || m_type == Mod::MOD_FOLDER) return false; @@ -124,7 +124,7 @@ bool Mod::enable(bool value) return true; } -bool Mod::destroy(QDir& index_dir) +auto Mod::destroy(QDir& index_dir) -> bool { auto n = name(); // FIXME: This can fail to remove the metadata if the @@ -136,12 +136,12 @@ bool Mod::destroy(QDir& index_dir) return FS::deletePath(m_file.filePath()); } -const ModDetails& Mod::details() const +auto Mod::details() const -> const ModDetails& { return m_localDetails ? *m_localDetails : invalidDetails; } -QString Mod::name() const +auto Mod::name() const -> QString { auto d_name = details().name; if (!d_name.isEmpty()) { @@ -150,22 +150,22 @@ QString Mod::name() const return m_name; } -QString Mod::version() const +auto Mod::version() const -> QString { return details().version; } -QString Mod::homeurl() const +auto Mod::homeurl() const -> QString { return details().homeurl; } -QString Mod::description() const +auto Mod::description() const -> QString { return details().description; } -QStringList Mod::authors() const +auto Mod::authors() const -> QStringList { return details().authors; } diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 1e7ed1ed..3a0ccfa6 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -37,36 +37,36 @@ public: Mod(const QFileInfo &file); explicit Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata); - QFileInfo fileinfo() const { return m_file; } - QDateTime dateTimeChanged() const { return m_changedDateTime; } - QString internal_id() const { return m_internal_id; } - ModType type() const { return m_type; } - bool fromMetadata() const { return m_from_metadata; } - bool enabled() const { return m_enabled; } + auto fileinfo() const -> QFileInfo { return m_file; } + auto dateTimeChanged() const -> QDateTime { return m_changedDateTime; } + auto internal_id() const -> QString { return m_internal_id; } + auto type() const -> ModType { return m_type; } + auto fromMetadata() const -> bool { return m_from_metadata; } + auto enabled() const -> bool { return m_enabled; } - bool valid() const { return m_type != MOD_UNKNOWN; } + auto valid() const -> bool { return m_type != MOD_UNKNOWN; } - const ModDetails& details() const; - QString name() const; - QString version() const; - QString homeurl() const; - QString description() const; - QStringList authors() const; + auto details() const -> const ModDetails&; + auto name() const -> QString; + auto version() const -> QString; + auto homeurl() const -> QString; + auto description() const -> QString; + auto authors() const -> QStringList; - const std::shared_ptr metadata() const { return details().metadata; }; - std::shared_ptr metadata() { return m_localDetails->metadata; }; + auto metadata() const -> const std::shared_ptr { return details().metadata; }; + auto metadata() -> std::shared_ptr { return m_localDetails->metadata; }; - bool enable(bool value); + auto enable(bool value) -> bool; // delete all the files of this mod - bool destroy(QDir& index_dir); + auto destroy(QDir& index_dir) -> bool; // change the mod's filesystem path (used by mod lists for *MAGIC* purposes) void repath(const QFileInfo &file); - bool shouldResolve() const { return !m_resolving && !m_resolved; } - bool isResolving() const { return m_resolving; } - int resolutionTicket() const { return m_resolutionTicket; } + auto shouldResolve() const -> bool { return !m_resolving && !m_resolved; } + auto isResolving() const -> bool { return m_resolving; } + auto resolutionTicket() const -> int { return m_resolutionTicket; } void setResolving(bool resolving, int resolutionTicket) { m_resolving = resolving; diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index 3c9b76a8..47207ada 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -30,7 +30,7 @@ void LocalModUpdateTask::executeTask() emitSucceeded(); } -bool LocalModUpdateTask::abort() +auto LocalModUpdateTask::abort() -> bool { emitAborted(); return true; diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h index 866089e9..15591b21 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h @@ -2,8 +2,8 @@ #include -#include "tasks/Task.h" #include "modplatform/ModIndex.h" +#include "tasks/Task.h" class LocalModUpdateTask : public Task { Q_OBJECT @@ -12,8 +12,8 @@ class LocalModUpdateTask : public Task { explicit LocalModUpdateTask(QDir mods_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version); - bool canAbort() const override { return true; } - bool abort() override; + auto canAbort() const -> bool override { return true; } + auto abort() -> bool override; protected slots: //! Entry point for tasks. diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 8fd74a3e..978be462 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -6,13 +6,13 @@ #include "toml.h" -#include "modplatform/ModIndex.h" #include "minecraft/mod/Mod.h" +#include "modplatform/ModIndex.h" namespace Packwiz { // Helpers -static inline QString indexFileName(QString const& mod_name) +static inline auto indexFileName(QString const& mod_name) -> QString { if(mod_name.endsWith(".toml")) return mod_name; @@ -161,8 +161,9 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod return {}; } - toml_table_t* table; + toml_table_t* table = nullptr; + // NOLINTNEXTLINE(modernize-avoid-c-arrays) char errbuf[200]; table = toml_parse(index_file.readAll().data(), errbuf, sizeof(errbuf)); @@ -201,7 +202,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod return {}; } - toml_table_t* mod_provider_table; + toml_table_t* mod_provider_table = nullptr; if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::FLAME)))) { mod.provider = Provider::FLAME; mod.file_id = intEntry(mod_provider_table, "file-id"); diff --git a/launcher/modplatform/packwiz/Packwiz_test.cpp b/launcher/modplatform/packwiz/Packwiz_test.cpp index 2e61c167..08de332d 100644 --- a/launcher/modplatform/packwiz/Packwiz_test.cpp +++ b/launcher/modplatform/packwiz/Packwiz_test.cpp @@ -1,8 +1,8 @@ #include #include -#include "TestUtil.h" #include "Packwiz.h" +#include "TestUtil.h" class PackwizTest : public QObject { Q_OBJECT From a99858c64d275303a9f91912a2732746ef6a3c8a Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 19 Apr 2022 21:10:12 -0300 Subject: [PATCH 15/25] refactor: move code out of ModIndex.h Now it's in ModIndex.cpp --- launcher/CMakeLists.txt | 3 +++ launcher/modplatform/ModIndex.cpp | 24 +++++++++++++++++++++ launcher/modplatform/ModIndex.h | 22 ++----------------- launcher/modplatform/flame/FlameAPI.h | 1 + launcher/modplatform/modrinth/ModrinthAPI.h | 1 + launcher/modplatform/packwiz/Packwiz.cpp | 11 +++++----- 6 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 launcher/modplatform/ModIndex.cpp diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 6c7b5e43..1bab7ecb 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -500,6 +500,9 @@ set(META_SOURCES ) set(API_SOURCES + modplatform/ModIndex.h + modplatform/ModIndex.cpp + modplatform/ModAPI.h modplatform/flame/FlameAPI.h diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp new file mode 100644 index 00000000..eb8be992 --- /dev/null +++ b/launcher/modplatform/ModIndex.cpp @@ -0,0 +1,24 @@ +#include "modplatform/ModIndex.h" + +namespace ModPlatform{ + +auto ProviderCapabilities::name(Provider p) -> const char* +{ + switch(p){ + case Provider::MODRINTH: + return "modrinth"; + case Provider::FLAME: + return "curseforge"; + } +} +auto ProviderCapabilities::hashType(Provider p) -> QString +{ + switch(p){ + case Provider::MODRINTH: + return "sha512"; + case Provider::FLAME: + return "murmur2"; + } +} + +} // namespace ModPlatform diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index ee623b78..bb5c7c9d 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -15,26 +15,8 @@ enum class Provider{ class ProviderCapabilities { public: - static QString hashType(Provider p) - { - switch(p){ - case Provider::MODRINTH: - return "sha512"; - case Provider::FLAME: - return "murmur2"; - } - return ""; - } - static const char* providerName(Provider p) - { - switch(p){ - case Provider::MODRINTH: - return "modrinth"; - case Provider::FLAME: - return "curseforge"; - } - return ""; - } + auto name(Provider) -> const char*; + auto hashType(Provider) -> QString; }; struct ModpackAuthor { diff --git a/launcher/modplatform/flame/FlameAPI.h b/launcher/modplatform/flame/FlameAPI.h index 8bb33d47..e31cf0a1 100644 --- a/launcher/modplatform/flame/FlameAPI.h +++ b/launcher/modplatform/flame/FlameAPI.h @@ -1,5 +1,6 @@ #pragma once +#include "modplatform/ModIndex.h" #include "modplatform/helpers/NetworkModAPI.h" class FlameAPI : public NetworkModAPI { diff --git a/launcher/modplatform/modrinth/ModrinthAPI.h b/launcher/modplatform/modrinth/ModrinthAPI.h index 79bc5175..f9d35fcd 100644 --- a/launcher/modplatform/modrinth/ModrinthAPI.h +++ b/launcher/modplatform/modrinth/ModrinthAPI.h @@ -20,6 +20,7 @@ #include "BuildConfig.h" #include "modplatform/ModAPI.h" +#include "modplatform/ModIndex.h" #include "modplatform/helpers/NetworkModAPI.h" #include diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 978be462..872da9b1 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -19,6 +19,8 @@ static inline auto indexFileName(QString const& mod_name) -> QString return QString("%1.toml").arg(mod_name); } +static ModPlatform::ProviderCapabilities ProviderCaps; + auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -27,7 +29,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.filename = mod_version.fileName; mod.url = mod_version.downloadUrl; - mod.hash_format = ModPlatform::ProviderCapabilities::hashType(mod_pack.provider); + mod.hash_format = ProviderCaps.hashType(mod_pack.provider); mod.hash = ""; // FIXME mod.provider = mod_pack.provider; @@ -92,7 +94,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) addToStream("hash", mod.hash); in_stream << QString("\n[update]\n"); - in_stream << QString("[update.%1]\n").arg(ModPlatform::ProviderCapabilities::providerName(mod.provider)); + in_stream << QString("[update.%1]\n").arg(ProviderCaps.name(mod.provider)); switch(mod.provider){ case(ModPlatform::Provider::FLAME): in_stream << QString("file-id = %1\n").arg(mod.file_id.toString()); @@ -193,7 +195,6 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod } { // [update] info - using ProviderCaps = ModPlatform::ProviderCapabilities; using Provider = ModPlatform::Provider; toml_table_t* update_table = toml_table_in(table, "update"); @@ -203,11 +204,11 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod } toml_table_t* mod_provider_table = nullptr; - if ((mod_provider_table = toml_table_in(update_table, ProviderCaps::providerName(Provider::FLAME)))) { + if ((mod_provider_table = toml_table_in(update_table, ProviderCaps.name(Provider::FLAME)))) { 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 = toml_table_in(update_table, ProviderCaps::providerName(Provider::MODRINTH)))) { + } else if ((mod_provider_table = toml_table_in(update_table, ProviderCaps.name(Provider::MODRINTH)))) { mod.provider = Provider::MODRINTH; mod.mod_id() = stringEntry(mod_provider_table, "mod-id"); mod.version() = stringEntry(mod_provider_table, "version"); From 96e36f060443cbfa6d58df2adca3c8605851b4a3 Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 20 Apr 2022 18:45:39 -0300 Subject: [PATCH 16/25] refactor: make mod metadata presence (or lack of) easier to find out --- launcher/minecraft/mod/Mod.cpp | 28 +++++++++++++++++-- launcher/minecraft/mod/Mod.h | 6 ++-- launcher/minecraft/mod/ModDetails.h | 9 ++++++ .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 8 +++++- launcher/modplatform/ModIndex.cpp | 2 ++ launcher/modplatform/packwiz/Packwiz.cpp | 9 ++---- 6 files changed, 49 insertions(+), 13 deletions(-) diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 992b91dc..261ae9d2 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -54,7 +54,6 @@ Mod::Mod(const QDir& mods_dir, const Metadata::ModStruct& metadata) m_type = MOD_SINGLEFILE; } - m_from_metadata = true; m_enabled = true; m_changedDateTime = m_file.lastModified(); @@ -117,13 +116,27 @@ auto Mod::enable(bool value) -> bool return false; } - if (!fromMetadata()) + if (status() == ModStatus::NoMetadata) repath(QFileInfo(path)); m_enabled = value; return true; } +void Mod::setStatus(ModStatus status) +{ + if(m_localDetails.get()) + m_localDetails->status = status; +} +void Mod::setMetadata(Metadata::ModStruct* metadata) +{ + if(status() == ModStatus::NoMetadata) + setStatus(ModStatus::Installed); + + if(m_localDetails.get()) + m_localDetails->metadata.reset(metadata); +} + auto Mod::destroy(QDir& index_dir) -> bool { auto n = name(); @@ -170,13 +183,22 @@ auto Mod::authors() const -> QStringList return details().authors; } +auto Mod::status() const -> ModStatus +{ + return details().status; +} + void Mod::finishResolvingWithDetails(std::shared_ptr details) { m_resolving = false; m_resolved = true; m_localDetails = details; - if (fromMetadata() && m_temp_metadata->isValid() && m_localDetails.get()) { + if (status() != ModStatus::NoMetadata + && m_temp_metadata.get() + && m_temp_metadata->isValid() && + m_localDetails.get()) { + m_localDetails->metadata.swap(m_temp_metadata); } } diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 3a0ccfa6..58c7a80f 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -41,7 +41,6 @@ public: auto dateTimeChanged() const -> QDateTime { return m_changedDateTime; } auto internal_id() const -> QString { return m_internal_id; } auto type() const -> ModType { return m_type; } - auto fromMetadata() const -> bool { return m_from_metadata; } auto enabled() const -> bool { return m_enabled; } auto valid() const -> bool { return m_type != MOD_UNKNOWN; } @@ -52,10 +51,14 @@ public: auto homeurl() const -> QString; auto description() const -> QString; auto authors() const -> QStringList; + auto status() const -> ModStatus; auto metadata() const -> const std::shared_ptr { return details().metadata; }; auto metadata() -> std::shared_ptr { return m_localDetails->metadata; }; + void setStatus(ModStatus status); + void setMetadata(Metadata::ModStruct* metadata); + auto enable(bool value) -> bool; // delete all the files of this mod @@ -82,7 +85,6 @@ protected: /* Name as reported via the file name */ QString m_name; ModType m_type = MOD_UNKNOWN; - bool m_from_metadata = false; /* If the mod has metadata, this will be filled in the constructor, and passed to * the ModDetails when calling finishResolvingWithDetails */ diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index f9973fc2..75ffea32 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -7,6 +7,12 @@ #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 +}; + struct ModDetails { /* Mod ID as defined in the ModLoader-specific metadata */ @@ -30,6 +36,9 @@ struct ModDetails /* List of the author's names */ QStringList authors; + /* Installation status of the mod */ + ModStatus status; + /* Metadata information, if any */ std::shared_ptr metadata; }; diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index 03a17461..addb0dd8 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -19,8 +19,13 @@ void ModFolderLoadTask::run() m_mods_dir.refresh(); for (auto entry : m_mods_dir.entryInfoList()) { Mod mod(entry); - if (!m_result->mods.contains(mod.internal_id())) + if(m_result->mods.contains(mod.internal_id())){ + m_result->mods[mod.internal_id()].setStatus(ModStatus::Installed); + } + else { m_result->mods[mod.internal_id()] = mod; + m_result->mods[mod.internal_id()].setStatus(ModStatus::NoMetadata); + } } emit succeeded(); @@ -42,6 +47,7 @@ void ModFolderLoadTask::getFromMetadata() } Mod mod(m_mods_dir, metadata); + mod.setStatus(ModStatus::NotInstalled); m_result->mods[mod.internal_id()] = mod; } } diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index eb8be992..b3c057fb 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -10,6 +10,7 @@ auto ProviderCapabilities::name(Provider p) -> const char* case Provider::FLAME: return "curseforge"; } + return {}; } auto ProviderCapabilities::hashType(Provider p) -> QString { @@ -19,6 +20,7 @@ auto ProviderCapabilities::hashType(Provider p) -> QString case Provider::FLAME: return "murmur2"; } + return {}; } } // namespace ModPlatform diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 872da9b1..50f87c24 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -48,14 +48,9 @@ auto V1::createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod if(mod.isValid()) return mod; - // Manually construct packwiz mod - mod.name = internal_mod.name(); - mod.filename = internal_mod.fileinfo().fileName(); + qWarning() << QString("Tried to create mod metadata with a Mod without metadata!"); - // TODO: Have a mechanism for telling the UI subsystem that we want to gather user information - // (i.e. which mod provider we want to use). Maybe an object parameter with a signal for that? - - return mod; + return {}; } void V1::updateModIndex(QDir& index_dir, Mod& mod) From e17b6804a7424dd5161662c4ef92972f3311675c Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 21 Apr 2022 15:45:20 -0300 Subject: [PATCH 17/25] fix: implement PR suggestions Some stylistic changes, and get hashes from the mod providers when building the metadata. --- launcher/Application.cpp | 2 +- launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp | 11 +++-------- launcher/modplatform/ModIndex.h | 3 ++- launcher/modplatform/flame/FlameModIndex.cpp | 8 ++++++++ launcher/modplatform/modrinth/ModrinthPackIndex.cpp | 4 ++++ launcher/modplatform/packwiz/Packwiz.cpp | 2 +- launcher/ui/pages/global/LauncherPage.cpp | 2 +- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ae4cbcf8..99e3d4c5 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -644,7 +644,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv) m_settings->registerSetting("MCLaunchMethod", "LauncherPart"); // Minecraft mods - m_settings->registerSetting("DontUseModMetadata", false); + m_settings->registerSetting("ModMetadataDisabled", false); // Minecraft offline player name m_settings->registerSetting("LastOfflinePlayerName", ""); diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index addb0dd8..fe807a29 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -10,7 +10,7 @@ ModFolderLoadTask::ModFolderLoadTask(QDir& mods_dir, QDir& index_dir) void ModFolderLoadTask::run() { - if (!APPLICATION->settings()->get("DontUseModMetadata").toBool()) { + if (!APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { // Read metadata first getFromMetadata(); } @@ -34,14 +34,9 @@ void ModFolderLoadTask::run() void ModFolderLoadTask::getFromMetadata() { m_index_dir.refresh(); - for (auto entry : m_index_dir.entryList()) { - // QDir::Filter::NoDotAndDotDot seems to exclude all files for some reason... - if (entry == "." || entry == "..") - continue; - + for (auto entry : m_index_dir.entryList(QDir::Files)) { auto metadata = Metadata::get(m_index_dir, entry); - // TODO: Don't simply return. Instead, show to the user that the metadata is there, but - // it's not currently 'installed' (i.e. there's no JAR file yet). + if(!metadata.isValid()){ return; } diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index bb5c7c9d..2137f616 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -8,7 +8,7 @@ namespace ModPlatform { -enum class Provider{ +enum class Provider { MODRINTH, FLAME }; @@ -33,6 +33,7 @@ struct IndexedVersion { QString date; QString fileName; QVector loaders = {}; + QString hash; }; struct IndexedPack { diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 45f02b71..4b172c13 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -6,6 +6,8 @@ #include "modplatform/flame/FlameAPI.h" #include "net/NetJob.h" +static ModPlatform::ProviderCapabilities ProviderCaps; + void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { pack.addonId = Json::requireInteger(obj, "id"); @@ -60,6 +62,12 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, file.downloadUrl = Json::requireString(obj, "downloadUrl"); file.fileName = Json::requireString(obj, "fileName"); + auto hash_list = Json::ensureArray(obj, "hashes"); + if(!hash_list.isEmpty()){ + if(hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::FLAME))) + file.hash = Json::requireString(hash_list, "value"); + } + unsortedVersions.append(file); } diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 6c8659dc..8b750740 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -24,6 +24,7 @@ #include "net/NetJob.h" static ModrinthAPI api; +static ModPlatform::ProviderCapabilities ProviderCaps; void Modrinth::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) { @@ -95,6 +96,9 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, if (parent.contains("url")) { file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); + auto hash_list = Json::requireObject(parent, "hashes"); + if(hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::MODRINTH))) + file.hash = Json::requireString(hash_list, ProviderCaps.hashType(ModPlatform::Provider::MODRINTH)); unsortedVersions.append(file); } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 50f87c24..70efc6bd 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -30,7 +30,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.url = mod_version.downloadUrl; mod.hash_format = ProviderCaps.hashType(mod_pack.provider); - mod.hash = ""; // FIXME + mod.hash = mod_version.hash; mod.provider = mod_pack.provider; mod.file_id = mod_pack.addonId; diff --git a/launcher/ui/pages/global/LauncherPage.cpp b/launcher/ui/pages/global/LauncherPage.cpp index 8754c0ec..faf9272d 100644 --- a/launcher/ui/pages/global/LauncherPage.cpp +++ b/launcher/ui/pages/global/LauncherPage.cpp @@ -345,7 +345,7 @@ void LauncherPage::applySettings() } // Mods - s->set("DontUseModMetadata", ui->metadataDisableBtn->isChecked()); + s->set("ModMetadataDisabled", ui->metadataDisableBtn->isChecked()); } void LauncherPage::loadSettings() { From 67e0214fa5c1ff36d3718c3fb68107bf0dfe7e5d Mon Sep 17 00:00:00 2001 From: flow Date: Thu, 21 Apr 2022 15:47:46 -0300 Subject: [PATCH 18/25] fix: don't try to delete mods multiple times Shows a more helpful message if there's a parsing error when reading the index file. Also fixes a clazy warning with using the `.data()` method in a temporary QByteArray object. --- launcher/minecraft/mod/ModFolderModel.cpp | 3 +++ launcher/modplatform/packwiz/Packwiz.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/launcher/minecraft/mod/ModFolderModel.cpp b/launcher/minecraft/mod/ModFolderModel.cpp index e034e35e..b2d8f03e 100644 --- a/launcher/minecraft/mod/ModFolderModel.cpp +++ b/launcher/minecraft/mod/ModFolderModel.cpp @@ -339,6 +339,9 @@ bool ModFolderModel::deleteMods(const QModelIndexList& indexes) for (auto i: indexes) { + if(i.column() != 0) { + continue; + } Mod &m = mods[i.row()]; auto index_dir = indexDir(); m.destroy(index_dir); diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 70efc6bd..4fe4398a 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -162,12 +162,14 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod // NOLINTNEXTLINE(modernize-avoid-c-arrays) char errbuf[200]; - table = toml_parse(index_file.readAll().data(), errbuf, sizeof(errbuf)); + auto file_bytearray = index_file.readAll(); + table = toml_parse(file_bytearray.data(), errbuf, sizeof(errbuf)); index_file.close(); if (!table) { - qCritical() << QString("Could not open file %1!").arg(indexFileName(mod.name)); + qWarning() << QString("Could not open file %1!").arg(indexFileName(index_file_name)); + qWarning() << "Reason: " << QString(errbuf); return {}; } From 5c5699bba5ed2a5befb7c3f8d9fbcd679a8698ab Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 22 Apr 2022 13:23:47 -0300 Subject: [PATCH 19/25] refactor: move individual pack version parsing to its own function --- .../modrinth/ModrinthPackIndex.cpp | 95 +++++++++++-------- .../modplatform/modrinth/ModrinthPackIndex.h | 1 + 2 files changed, 55 insertions(+), 41 deletions(-) diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 8b750740..aa798381 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -59,49 +59,10 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, for (auto versionIter : arr) { auto obj = versionIter.toObject(); - ModPlatform::IndexedVersion file; - file.addonId = Json::requireString(obj, "project_id"); - file.fileId = Json::requireString(obj, "id"); - file.date = Json::requireString(obj, "date_published"); - auto versionArray = Json::requireArray(obj, "game_versions"); - if (versionArray.empty()) { continue; } - for (auto mcVer : versionArray) { - file.mcVersion.append(mcVer.toString()); - } - auto loaders = Json::requireArray(obj, "loaders"); - for (auto loader : loaders) { - file.loaders.append(loader.toString()); - } - file.version = Json::requireString(obj, "name"); - - auto files = Json::requireArray(obj, "files"); - int i = 0; - - // Find correct file (needed in cases where one version may have multiple files) - // Will default to the last one if there's no primary (though I think Modrinth requires that - // at least one file is primary, idk) - // NOTE: files.count() is 1-indexed, so we need to subtract 1 to become 0-indexed - while (i < files.count() - 1){ - auto parent = files[i].toObject(); - auto fileName = Json::requireString(parent, "filename"); - - // Grab the primary file, if available - if(Json::requireBoolean(parent, "primary")) - break; - - i++; - } - - auto parent = files[i].toObject(); - if (parent.contains("url")) { - file.downloadUrl = Json::requireString(parent, "url"); - file.fileName = Json::requireString(parent, "filename"); - auto hash_list = Json::requireObject(parent, "hashes"); - if(hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::MODRINTH))) - file.hash = Json::requireString(hash_list, ProviderCaps.hashType(ModPlatform::Provider::MODRINTH)); + auto file = loadIndexedPackVersion(obj); + if(file.fileId.isValid()) // Heuristic to check if the returned value is valid unsortedVersions.append(file); - } } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { // dates are in RFC 3339 format @@ -111,3 +72,55 @@ void Modrinth::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, pack.versions = unsortedVersions; pack.versionsLoaded = true; } + +auto Modrinth::loadIndexedPackVersion(QJsonObject &obj) -> ModPlatform::IndexedVersion +{ + ModPlatform::IndexedVersion file; + + file.addonId = Json::requireString(obj, "project_id"); + file.fileId = Json::requireString(obj, "id"); + file.date = Json::requireString(obj, "date_published"); + auto versionArray = Json::requireArray(obj, "game_versions"); + if (versionArray.empty()) { + return {}; + } + for (auto mcVer : versionArray) { + file.mcVersion.append(mcVer.toString()); + } + auto loaders = Json::requireArray(obj, "loaders"); + for (auto loader : loaders) { + file.loaders.append(loader.toString()); + } + file.version = Json::requireString(obj, "name"); + + auto files = Json::requireArray(obj, "files"); + int i = 0; + + // Find correct file (needed in cases where one version may have multiple files) + // Will default to the last one if there's no primary (though I think Modrinth requires that + // at least one file is primary, idk) + // NOTE: files.count() is 1-indexed, so we need to subtract 1 to become 0-indexed + while (i < files.count() - 1) { + auto parent = files[i].toObject(); + auto fileName = Json::requireString(parent, "filename"); + + // Grab the primary file, if available + if (Json::requireBoolean(parent, "primary")) + break; + + i++; + } + + auto parent = files[i].toObject(); + if (parent.contains("url")) { + file.downloadUrl = Json::requireString(parent, "url"); + file.fileName = Json::requireString(parent, "filename"); + auto hash_list = Json::requireObject(parent, "hashes"); + if (hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::MODRINTH))) + file.hash = Json::requireString(hash_list, ProviderCaps.hashType(ModPlatform::Provider::MODRINTH)); + + return file; + } + + return {}; +} diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.h b/launcher/modplatform/modrinth/ModrinthPackIndex.h index 7f306f25..df70278f 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.h +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.h @@ -29,5 +29,6 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const shared_qobject_ptr& network, BaseInstance* inst); +auto loadIndexedPackVersion(QJsonObject& obj) -> ModPlatform::IndexedVersion; } // namespace Modrinth From 59d628208b403bfb2442291cbca139cbdfcd325f Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 6 May 2022 12:42:01 -0300 Subject: [PATCH 20/25] feat: allow trying to use multiple hash types --- launcher/modplatform/ModIndex.cpp | 60 +++++++++++++++---- launcher/modplatform/ModIndex.h | 5 +- launcher/modplatform/flame/FlameModIndex.cpp | 10 +++- .../modrinth/ModrinthPackIndex.cpp | 10 +++- launcher/modplatform/packwiz/Packwiz.cpp | 2 +- 5 files changed, 68 insertions(+), 19 deletions(-) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index b3c057fb..f6e134e0 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -1,26 +1,60 @@ #include "modplatform/ModIndex.h" -namespace ModPlatform{ +#include + +namespace ModPlatform { auto ProviderCapabilities::name(Provider p) -> const char* { - switch(p){ - case Provider::MODRINTH: - return "modrinth"; - case Provider::FLAME: - return "curseforge"; + switch (p) { + case Provider::MODRINTH: + return "modrinth"; + case Provider::FLAME: + return "curseforge"; } return {}; } -auto ProviderCapabilities::hashType(Provider p) -> QString +auto ProviderCapabilities::readableName(Provider p) -> QString { - switch(p){ - case Provider::MODRINTH: - return "sha512"; - case Provider::FLAME: - return "murmur2"; + switch (p) { + case Provider::MODRINTH: + return "Modrinth"; + case Provider::FLAME: + return "CurseForge"; + } + return {}; +} +auto ProviderCapabilities::hashType(Provider p) -> QStringList +{ + switch (p) { + case Provider::MODRINTH: + return { "sha512", "sha1" }; + case Provider::FLAME: + return { "murmur2" }; + } + return {}; +} +auto ProviderCapabilities::hash(Provider p, QByteArray& data, QString type) -> QByteArray +{ + switch (p) { + case Provider::MODRINTH: { + // NOTE: Data is the result of reading the entire JAR file! + + // If 'type' was specified, we use that + if (!type.isEmpty() && hashType(p).contains(type)) { + if (type == "sha512") + return QCryptographicHash::hash(data, QCryptographicHash::Sha512); + else if (type == "sha1") + return QCryptographicHash::hash(data, QCryptographicHash::Sha1); + } + + return QCryptographicHash::hash(data, QCryptographicHash::Sha512); + } + case Provider::FLAME: + // TODO + break; } return {}; } -} // namespace ModPlatform +} // namespace ModPlatform diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 2137f616..8ada1fc6 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -16,7 +16,9 @@ enum class Provider { class ProviderCapabilities { public: auto name(Provider) -> const char*; - auto hashType(Provider) -> QString; + auto readableName(Provider) -> QString; + auto hashType(Provider) -> QStringList; + auto hash(Provider, QByteArray&, QString type = "") -> QByteArray; }; struct ModpackAuthor { @@ -33,6 +35,7 @@ struct IndexedVersion { QString date; QString fileName; QVector loaders = {}; + QString hash_type; QString hash; }; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 4b172c13..63411275 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -64,8 +64,14 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, auto hash_list = Json::ensureArray(obj, "hashes"); if(!hash_list.isEmpty()){ - if(hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::FLAME))) - file.hash = Json::requireString(hash_list, "value"); + auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::FLAME); + for(auto& hash_type : hash_types) { + if(hash_list.contains(hash_type)) { + file.hash = Json::requireString(hash_list, "value"); + file.hash_type = hash_type; + break; + } + } } unsortedVersions.append(file); diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index aa798381..30693a82 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -116,8 +116,14 @@ auto Modrinth::loadIndexedPackVersion(QJsonObject &obj) -> ModPlatform::IndexedV file.downloadUrl = Json::requireString(parent, "url"); file.fileName = Json::requireString(parent, "filename"); auto hash_list = Json::requireObject(parent, "hashes"); - if (hash_list.contains(ProviderCaps.hashType(ModPlatform::Provider::MODRINTH))) - file.hash = Json::requireString(hash_list, ProviderCaps.hashType(ModPlatform::Provider::MODRINTH)); + auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::MODRINTH); + for (auto& hash_type : hash_types) { + if (hash_list.contains(hash_type)) { + file.hash = Json::requireString(hash_list, hash_type); + file.hash_type = hash_type; + break; + } + } return file; } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 4fe4398a..cb430c1f 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -29,7 +29,7 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.filename = mod_version.fileName; mod.url = mod_version.downloadUrl; - mod.hash_format = ProviderCaps.hashType(mod_pack.provider); + mod.hash_format = mod_version.hash_type; mod.hash = mod_version.hash; mod.provider = mod_pack.provider; From 0985adfd74758891c2e61c2de7f930119cab1386 Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 7 May 2022 19:39:00 -0300 Subject: [PATCH 21/25] change: support newest changes with packwiz regarding CF --- launcher/modplatform/ModIndex.cpp | 12 +++++++-- launcher/modplatform/flame/FlameModIndex.cpp | 25 +++++++++++++----- launcher/modplatform/packwiz/Packwiz.cpp | 15 ++++++++--- launcher/modplatform/packwiz/Packwiz.h | 6 ++--- launcher/modplatform/packwiz/Packwiz_test.cpp | 6 ++--- ...-mining.toml => borderless-mining.pw.toml} | Bin ...=> screenshot-to-clipboard-fabric.pw.toml} | Bin 7 files changed, 46 insertions(+), 18 deletions(-) rename launcher/modplatform/packwiz/testdata/{borderless-mining.toml => borderless-mining.pw.toml} (100%) rename launcher/modplatform/packwiz/testdata/{screenshot-to-clipboard-fabric.toml => screenshot-to-clipboard-fabric.pw.toml} (100%) diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index f6e134e0..6027c4f3 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -30,7 +30,8 @@ auto ProviderCapabilities::hashType(Provider p) -> QStringList case Provider::MODRINTH: return { "sha512", "sha1" }; case Provider::FLAME: - return { "murmur2" }; + // Try newer formats first, fall back to old format + return { "sha1", "md5", "murmur2" }; } return {}; } @@ -51,7 +52,14 @@ auto ProviderCapabilities::hash(Provider p, QByteArray& data, QString type) -> Q return QCryptographicHash::hash(data, QCryptographicHash::Sha512); } case Provider::FLAME: - // TODO + // If 'type' was specified, we use that + if (!type.isEmpty() && hashType(p).contains(type)) { + if(type == "sha1") + return QCryptographicHash::hash(data, QCryptographicHash::Sha1); + else if (type == "md5") + return QCryptographicHash::hash(data, QCryptographicHash::Md5); + } + break; } return {}; diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 63411275..00dac411 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -30,6 +30,17 @@ void FlameMod::loadIndexedPack(ModPlatform::IndexedPack& pack, QJsonObject& obj) } } +static QString enumToString(int hash_algorithm) +{ + switch(hash_algorithm){ + default: + case 1: + return "sha1"; + case 2: + return "md5"; + } +} + void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const shared_qobject_ptr& network, @@ -63,14 +74,14 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, file.fileName = Json::requireString(obj, "fileName"); auto hash_list = Json::ensureArray(obj, "hashes"); - if(!hash_list.isEmpty()){ + for(auto h : hash_list){ + auto hash_entry = Json::ensureObject(h); auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::FLAME); - for(auto& hash_type : hash_types) { - if(hash_list.contains(hash_type)) { - file.hash = Json::requireString(hash_list, "value"); - file.hash_type = hash_type; - break; - } + auto hash_algo = enumToString(Json::ensureInteger(hash_entry, "algo", 1, "algorithm")); + if(hash_types.contains(hash_algo)){ + file.hash = Json::requireString(hash_entry, "value"); + file.hash_type = hash_algo; + break; } } diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index cb430c1f..1ad6d75b 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -14,9 +14,9 @@ namespace Packwiz { // Helpers static inline auto indexFileName(QString const& mod_name) -> QString { - if(mod_name.endsWith(".toml")) + if(mod_name.endsWith(".pw.toml")) return mod_name; - return QString("%1.toml").arg(mod_name); + return QString("%1.pw.toml").arg(mod_name); } static ModPlatform::ProviderCapabilities ProviderCaps; @@ -28,7 +28,14 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.name = mod_pack.name; mod.filename = mod_version.fileName; - mod.url = mod_version.downloadUrl; + if(mod_pack.provider == ModPlatform::Provider::FLAME){ + mod.mode = "metadata:curseforge"; + } + else { + mod.mode = "url"; + mod.url = mod_version.downloadUrl; + } + mod.hash_format = mod_version.hash_type; mod.hash = mod_version.hash; @@ -84,6 +91,7 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) addToStream("side", mod.side); in_stream << QString("\n[download]\n"); + addToStream("mode", mod.mode); addToStream("url", mod.url.toString()); addToStream("hash-format", mod.hash_format); addToStream("hash", mod.hash); @@ -186,6 +194,7 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod return {}; } + mod.mode = stringEntry(download_table, "mode"); mod.url = stringEntry(download_table, "url"); mod.hash_format = stringEntry(download_table, "hash-format"); mod.hash = stringEntry(download_table, "hash"); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 69125dbc..e66d0030 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -22,8 +22,8 @@ class V1 { QString side {"both"}; // [download] + QString mode {}; QUrl url {}; - // FIXME: make hash-format an enum QString hash_format {}; QString hash {}; @@ -42,11 +42,11 @@ class V1 { auto version() -> QVariant& { return file_id; } }; - /* Generates the object representing the information in a mod.toml file via + /* 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; - /* Generates the object representing the information in a mod.toml file via + /* Generates the object representing the information in a mod.pw.toml file via * its common representation in the launcher. * */ static auto createModFormat(QDir& index_dir, ::Mod& internal_mod) -> Mod; diff --git a/launcher/modplatform/packwiz/Packwiz_test.cpp b/launcher/modplatform/packwiz/Packwiz_test.cpp index 08de332d..9f3c486e 100644 --- a/launcher/modplatform/packwiz/Packwiz_test.cpp +++ b/launcher/modplatform/packwiz/Packwiz_test.cpp @@ -14,7 +14,7 @@ class PackwizTest : public QObject { QString source = QFINDTESTDATA("testdata"); QDir index_dir(source); - QString name_mod("borderless-mining.toml"); + QString name_mod("borderless-mining.pw.toml"); QVERIFY(index_dir.entryList().contains(name_mod)); auto metadata = Packwiz::V1::getIndexForMod(index_dir, name_mod); @@ -39,10 +39,10 @@ class PackwizTest : public QObject { QString source = QFINDTESTDATA("testdata"); QDir index_dir(source); - QString name_mod("screenshot-to-clipboard-fabric.toml"); + QString name_mod("screenshot-to-clipboard-fabric.pw.toml"); QVERIFY(index_dir.entryList().contains(name_mod)); - // Try without the .toml at the end + // Try without the .pw.toml at the end name_mod.chop(5); auto metadata = Packwiz::V1::getIndexForMod(index_dir, name_mod); diff --git a/launcher/modplatform/packwiz/testdata/borderless-mining.toml b/launcher/modplatform/packwiz/testdata/borderless-mining.pw.toml similarity index 100% rename from launcher/modplatform/packwiz/testdata/borderless-mining.toml rename to launcher/modplatform/packwiz/testdata/borderless-mining.pw.toml diff --git a/launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.toml b/launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.pw.toml similarity index 100% rename from launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.toml rename to launcher/modplatform/packwiz/testdata/screenshot-to-clipboard-fabric.pw.toml From 3a923060ceee142987e585d7ab4d78642f3506da Mon Sep 17 00:00:00 2001 From: flow Date: Sat, 7 May 2022 10:27:01 -0300 Subject: [PATCH 22/25] fix: use correct hash_type when creating metadata also fix: wrong parameter name in LocalModUpdateTask's constructor also fix: correct hash_format in CF --- launcher/minecraft/mod/tasks/LocalModUpdateTask.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h index 15591b21..f21c0b06 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h @@ -10,7 +10,7 @@ class LocalModUpdateTask : public Task { public: using Ptr = shared_qobject_ptr; - explicit LocalModUpdateTask(QDir mods_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version); + explicit LocalModUpdateTask(QDir index_dir, ModPlatform::IndexedPack& mod, ModPlatform::IndexedVersion& mod_version); auto canAbort() const -> bool override { return true; } auto abort() -> bool override; From 2fc1b999117ceebc3ebf05d96b0a749e3a0f9e98 Mon Sep 17 00:00:00 2001 From: flow Date: Tue, 10 May 2022 19:57:47 -0300 Subject: [PATCH 23/25] chore: add license headers Prevents a massive inload of Scrumplex ditto's :) I didn't add it to every file modified in this PR because the other changes are pretty minor, and would explode the diff of the PR. I hope that's not a problem O_O --- launcher/ModDownloadTask.cpp | 20 +++++++- launcher/ModDownloadTask.h | 27 +++++++++-- launcher/minecraft/mod/MetadataHandler.h | 18 +++++++ launcher/minecraft/mod/Mod.cpp | 48 +++++++++++++------ launcher/minecraft/mod/Mod.h | 48 +++++++++++++------ launcher/minecraft/mod/ModDetails.h | 35 ++++++++++++++ .../mod/tasks/LocalModUpdateTask.cpp | 20 +++++++- .../minecraft/mod/tasks/LocalModUpdateTask.h | 18 +++++++ .../minecraft/mod/tasks/ModFolderLoadTask.cpp | 36 +++++++++++++- .../minecraft/mod/tasks/ModFolderLoadTask.h | 35 ++++++++++++++ launcher/modplatform/ModIndex.cpp | 18 +++++++ launcher/modplatform/ModIndex.h | 18 +++++++ .../modrinth/ModrinthPackIndex.cpp | 29 +++++------ launcher/modplatform/packwiz/Packwiz.cpp | 18 +++++++ launcher/modplatform/packwiz/Packwiz.h | 18 +++++++ launcher/modplatform/packwiz/Packwiz_test.cpp | 18 +++++++ 16 files changed, 373 insertions(+), 51 deletions(-) diff --git a/launcher/ModDownloadTask.cpp b/launcher/ModDownloadTask.cpp index 52de9c94..301b6637 100644 --- a/launcher/ModDownloadTask.cpp +++ b/launcher/ModDownloadTask.cpp @@ -1,7 +1,25 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #include "ModDownloadTask.h" #include "Application.h" -#include "minecraft/mod/tasks/LocalModUpdateTask.h" +#include "minecraft/mod/ModFolderModel.h" ModDownloadTask::ModDownloadTask(ModPlatform::IndexedPack mod, ModPlatform::IndexedVersion version, const std::shared_ptr mods) : m_mod(mod), m_mod_version(version), mods(mods) diff --git a/launcher/ModDownloadTask.h b/launcher/ModDownloadTask.h index 5eaee187..f4438a8d 100644 --- a/launcher/ModDownloadTask.h +++ b/launcher/ModDownloadTask.h @@ -1,14 +1,31 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #pragma once -#include -#include "QObjectPtr.h" -#include "minecraft/mod/ModFolderModel.h" -#include "modplatform/ModIndex.h" #include "net/NetJob.h" - #include "tasks/SequentialTask.h" + +#include "modplatform/ModIndex.h" #include "minecraft/mod/tasks/LocalModUpdateTask.h" +class ModFolderModel; + class ModDownloadTask : public SequentialTask { Q_OBJECT public: diff --git a/launcher/minecraft/mod/MetadataHandler.h b/launcher/minecraft/mod/MetadataHandler.h index 26b1f799..56962818 100644 --- a/launcher/minecraft/mod/MetadataHandler.h +++ b/launcher/minecraft/mod/MetadataHandler.h @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #pragma once #include diff --git a/launcher/minecraft/mod/Mod.cpp b/launcher/minecraft/mod/Mod.cpp index 261ae9d2..71a32d32 100644 --- a/launcher/minecraft/mod/Mod.cpp +++ b/launcher/minecraft/mod/Mod.cpp @@ -1,17 +1,37 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* This file incorporates work covered by the following copyright and +* permission notice: +* +* Copyright 2013-2021 MultiMC Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ #include "Mod.h" diff --git a/launcher/minecraft/mod/Mod.h b/launcher/minecraft/mod/Mod.h index 58c7a80f..96d471b4 100644 --- a/launcher/minecraft/mod/Mod.h +++ b/launcher/minecraft/mod/Mod.h @@ -1,17 +1,37 @@ -/* Copyright 2013-2021 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* This file incorporates work covered by the following copyright and +* permission notice: +* +* Copyright 2013-2021 MultiMC Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ #pragma once diff --git a/launcher/minecraft/mod/ModDetails.h b/launcher/minecraft/mod/ModDetails.h index 75ffea32..3e0a7ab0 100644 --- a/launcher/minecraft/mod/ModDetails.h +++ b/launcher/minecraft/mod/ModDetails.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* This file incorporates work covered by the following copyright and +* permission notice: +* +* Copyright 2013-2021 MultiMC Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + #pragma once #include diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp index 47207ada..cbe16567 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.cpp @@ -1,6 +1,22 @@ -#include "LocalModUpdateTask.h" +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ -#include +#include "LocalModUpdateTask.h" #include "Application.h" #include "FileSystem.h" diff --git a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h index f21c0b06..2db183e0 100644 --- a/launcher/minecraft/mod/tasks/LocalModUpdateTask.h +++ b/launcher/minecraft/mod/tasks/LocalModUpdateTask.h @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #pragma once #include diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp index fe807a29..62d856f6 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.cpp @@ -1,5 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* This file incorporates work covered by the following copyright and +* permission notice: +* +* Copyright 2013-2021 MultiMC Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + #include "ModFolderLoadTask.h" -#include #include "Application.h" #include "minecraft/mod/MetadataHandler.h" diff --git a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h index ba997874..89a0f84e 100644 --- a/launcher/minecraft/mod/tasks/ModFolderLoadTask.h +++ b/launcher/minecraft/mod/tasks/ModFolderLoadTask.h @@ -1,3 +1,38 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* This file incorporates work covered by the following copyright and +* permission notice: +* +* Copyright 2013-2021 MultiMC Contributors +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + #pragma once #include diff --git a/launcher/modplatform/ModIndex.cpp b/launcher/modplatform/ModIndex.cpp index 6027c4f3..3c4b7887 100644 --- a/launcher/modplatform/ModIndex.cpp +++ b/launcher/modplatform/ModIndex.cpp @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #include "modplatform/ModIndex.h" #include diff --git a/launcher/modplatform/ModIndex.h b/launcher/modplatform/ModIndex.h index 8ada1fc6..04dd2dac 100644 --- a/launcher/modplatform/ModIndex.h +++ b/launcher/modplatform/ModIndex.h @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #pragma once #include diff --git a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp index 30693a82..fdce71c3 100644 --- a/launcher/modplatform/modrinth/ModrinthPackIndex.cpp +++ b/launcher/modplatform/modrinth/ModrinthPackIndex.cpp @@ -1,19 +1,20 @@ // SPDX-License-Identifier: GPL-3.0-only /* - * PolyMC - Minecraft Launcher - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ #include "ModrinthPackIndex.h" #include "ModrinthAPI.h" diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 1ad6d75b..91a5f9c6 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #include "Packwiz.h" #include diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index e66d0030..58b86484 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #pragma once #include "modplatform/ModIndex.h" diff --git a/launcher/modplatform/packwiz/Packwiz_test.cpp b/launcher/modplatform/packwiz/Packwiz_test.cpp index 9f3c486e..023b990e 100644 --- a/launcher/modplatform/packwiz/Packwiz_test.cpp +++ b/launcher/modplatform/packwiz/Packwiz_test.cpp @@ -1,3 +1,21 @@ +// SPDX-License-Identifier: GPL-3.0-only +/* +* PolyMC - Minecraft Launcher +* Copyright (c) 2022 flowln +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + #include #include From 42f8ec5b1489c2073adf9d3526080c434dbddd90 Mon Sep 17 00:00:00 2001 From: flow Date: Fri, 13 May 2022 11:42:08 -0300 Subject: [PATCH 24/25] fix: do modrinth changes on flame too Also fix a dumb moment --- launcher/modplatform/flame/FlameModIndex.cpp | 75 +++++++++++--------- launcher/modplatform/flame/FlameModIndex.h | 1 + launcher/modplatform/packwiz/Packwiz.cpp | 4 +- 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/launcher/modplatform/flame/FlameModIndex.cpp b/launcher/modplatform/flame/FlameModIndex.cpp index 00dac411..ed6d64c3 100644 --- a/launcher/modplatform/flame/FlameModIndex.cpp +++ b/launcher/modplatform/flame/FlameModIndex.cpp @@ -52,40 +52,13 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, for (auto versionIter : arr) { auto obj = versionIter.toObject(); + + auto file = loadIndexedPackVersion(obj); + if(!file.addonId.isValid()) + file.addonId = pack.addonId; - auto versionArray = Json::requireArray(obj, "gameVersions"); - if (versionArray.isEmpty()) { - continue; - } - - ModPlatform::IndexedVersion file; - for (auto mcVer : versionArray) { - auto str = mcVer.toString(); - - if (str.contains('.')) - file.mcVersion.append(str); - } - - file.addonId = pack.addonId; - file.fileId = Json::requireInteger(obj, "id"); - file.date = Json::requireString(obj, "fileDate"); - file.version = Json::requireString(obj, "displayName"); - file.downloadUrl = Json::requireString(obj, "downloadUrl"); - file.fileName = Json::requireString(obj, "fileName"); - - auto hash_list = Json::ensureArray(obj, "hashes"); - for(auto h : hash_list){ - auto hash_entry = Json::ensureObject(h); - auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::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"); - file.hash_type = hash_algo; - break; - } - } - - unsortedVersions.append(file); + if(file.fileId.isValid()) // Heuristic to check if the returned value is valid + unsortedVersions.append(file); } auto orderSortPredicate = [](const ModPlatform::IndexedVersion& a, const ModPlatform::IndexedVersion& b) -> bool { @@ -96,3 +69,39 @@ void FlameMod::loadIndexedPackVersions(ModPlatform::IndexedPack& pack, pack.versions = unsortedVersions; pack.versionsLoaded = true; } + +auto FlameMod::loadIndexedPackVersion(QJsonObject& obj) -> ModPlatform::IndexedVersion +{ + auto versionArray = Json::requireArray(obj, "gameVersions"); + if (versionArray.isEmpty()) { + return {}; + } + + ModPlatform::IndexedVersion file; + for (auto mcVer : versionArray) { + auto str = mcVer.toString(); + + if (str.contains('.')) + file.mcVersion.append(str); + } + + file.addonId = Json::requireInteger(obj, "modId"); + file.fileId = Json::requireInteger(obj, "id"); + file.date = Json::requireString(obj, "fileDate"); + file.version = Json::requireString(obj, "displayName"); + file.downloadUrl = Json::requireString(obj, "downloadUrl"); + file.fileName = Json::requireString(obj, "fileName"); + + auto hash_list = Json::ensureArray(obj, "hashes"); + for (auto h : hash_list) { + auto hash_entry = Json::ensureObject(h); + auto hash_types = ProviderCaps.hashType(ModPlatform::Provider::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"); + file.hash_type = hash_algo; + break; + } + } + return file; +} diff --git a/launcher/modplatform/flame/FlameModIndex.h b/launcher/modplatform/flame/FlameModIndex.h index d3171d94..2e0f2e86 100644 --- a/launcher/modplatform/flame/FlameModIndex.h +++ b/launcher/modplatform/flame/FlameModIndex.h @@ -16,5 +16,6 @@ void loadIndexedPackVersions(ModPlatform::IndexedPack& pack, QJsonArray& arr, const shared_qobject_ptr& network, BaseInstance* inst); +auto loadIndexedPackVersion(QJsonObject& obj) -> ModPlatform::IndexedVersion; } // namespace FlameMod diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index 91a5f9c6..ee82f8a0 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -58,8 +58,8 @@ auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, Mo mod.hash = mod_version.hash; mod.provider = mod_pack.provider; - mod.file_id = mod_pack.addonId; - mod.project_id = mod_version.fileId; + mod.file_id = mod_version.fileId; + mod.project_id = mod_pack.addonId; return mod; } From 5a1de15332bcfbeafff7d0c678d7286ca85cfe18 Mon Sep 17 00:00:00 2001 From: flow Date: Wed, 18 May 2022 05:46:07 -0300 Subject: [PATCH 25/25] fix: use a more robust method of finding metadata indexes Often times, mods can have their name in different forms, changing one letter to caps or the other way (e.g. JourneyMaps -> Journeymaps). This makes it possible to find those as well, which is not perfect by any means, but should suffice for the majority of cases. --- launcher/modplatform/packwiz/Packwiz.cpp | 110 +++++++++++++++-------- launcher/modplatform/packwiz/Packwiz.h | 6 ++ 2 files changed, 80 insertions(+), 36 deletions(-) diff --git a/launcher/modplatform/packwiz/Packwiz.cpp b/launcher/modplatform/packwiz/Packwiz.cpp index ee82f8a0..0782b9f4 100644 --- a/launcher/modplatform/packwiz/Packwiz.cpp +++ b/launcher/modplatform/packwiz/Packwiz.cpp @@ -23,12 +23,37 @@ #include #include "toml.h" +#include "FileSystem.h" #include "minecraft/mod/Mod.h" #include "modplatform/ModIndex.h" namespace Packwiz { +auto getRealIndexName(QDir& index_dir, QString normalized_fname, bool should_find_match) -> QString +{ + QFile index_file(index_dir.absoluteFilePath(normalized_fname)); + + QString real_fname = normalized_fname; + if (!index_file.exists()) { + // Tries to get similar entries + for (auto& file_name : index_dir.entryList(QDir::Filter::Files)) { + if (!QString::compare(normalized_fname, file_name, Qt::CaseInsensitive)) { + real_fname = file_name; + break; + } + } + + if(should_find_match && !QString::compare(normalized_fname, real_fname, Qt::CaseSensitive)){ + qCritical() << "Could not find a match for a valid metadata file!"; + qCritical() << "File: " << normalized_fname; + return {}; + } + } + + return real_fname; +} + // Helpers static inline auto indexFileName(QString const& mod_name) -> QString { @@ -39,6 +64,33 @@ static inline auto indexFileName(QString const& mod_name) -> QString static ModPlatform::ProviderCapabilities ProviderCaps; +// Helper functions for extracting data from the TOML file +auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString +{ + toml_datum_t var = toml_string_in(parent, entry_name); + if (!var.ok) { + qCritical() << QString("Failed to read str property '%1' in mod metadata.").arg(entry_name); + return {}; + } + + QString tmp = var.u.s; + free(var.u.s); + + return tmp; +} + +auto intEntry(toml_table_t* parent, const char* entry_name) -> int +{ + toml_datum_t var = toml_int_in(parent, entry_name); + if (!var.ok) { + qCritical() << QString("Failed to read int property '%1' in mod metadata.").arg(entry_name); + return {}; + } + + return var.u.i; +} + + auto V1::createModFormat(QDir& index_dir, ModPlatform::IndexedPack& mod_pack, ModPlatform::IndexedVersion& mod_version) -> Mod { Mod mod; @@ -86,7 +138,11 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) } // Ensure the corresponding mod's info exists, and create it if not - QFile index_file(index_dir.absoluteFilePath(indexFileName(mod.name))); + + auto normalized_fname = indexFileName(mod.name); + auto real_fname = getRealIndexName(index_dir, normalized_fname); + + QFile index_file(index_dir.absoluteFilePath(real_fname)); // There's already data on there! // TODO: We should do more stuff here, as the user is likely trying to @@ -127,11 +183,18 @@ void V1::updateModIndex(QDir& index_dir, Mod& mod) break; } } + + index_file.close(); } void V1::deleteModIndex(QDir& index_dir, QString& mod_name) { - QFile index_file(index_dir.absoluteFilePath(indexFileName(mod_name))); + auto normalized_fname = indexFileName(mod_name); + auto real_fname = getRealIndexName(index_dir, normalized_fname); + if (real_fname.isEmpty()) + return; + + QFile index_file(index_dir.absoluteFilePath(real_fname)); if(!index_file.exists()){ qWarning() << QString("Tried to delete non-existent mod metadata for %1!").arg(mod_name); @@ -143,42 +206,17 @@ void V1::deleteModIndex(QDir& index_dir, QString& mod_name) } } -// Helper functions for extracting data from the TOML file -static auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString -{ - toml_datum_t var = toml_string_in(parent, entry_name); - if (!var.ok) { - qCritical() << QString("Failed to read str property '%1' in mod metadata.").arg(entry_name); - return {}; - } - - QString tmp = var.u.s; - free(var.u.s); - - return tmp; -} - -static auto intEntry(toml_table_t* parent, const char* entry_name) -> int -{ - toml_datum_t var = toml_int_in(parent, entry_name); - if (!var.ok) { - qCritical() << QString("Failed to read int property '%1' in mod metadata.").arg(entry_name); - return {}; - } - - return var.u.i; -} - auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod { Mod mod; - QFile index_file(index_dir.absoluteFilePath(indexFileName(index_file_name))); - - if (!index_file.exists()) { - qWarning() << QString("Tried to get a non-existent mod metadata for %1").arg(index_file_name); + auto normalized_fname = indexFileName(index_file_name); + auto real_fname = getRealIndexName(index_dir, normalized_fname, true); + if (real_fname.isEmpty()) return {}; - } + + QFile index_file(index_dir.absoluteFilePath(real_fname)); + if (!index_file.open(QIODevice::ReadOnly)) { qWarning() << QString("Failed to open mod metadata for %1").arg(index_file_name); return {}; @@ -198,14 +236,14 @@ auto V1::getIndexForMod(QDir& index_dir, QString& index_file_name) -> Mod qWarning() << "Reason: " << QString(errbuf); return {}; } - - { // Basic info + + { // Basic info mod.name = stringEntry(table, "name"); mod.filename = stringEntry(table, "filename"); mod.side = stringEntry(table, "side"); } - { // [download] info + { // [download] info toml_table_t* download_table = toml_table_in(table, "download"); if (!download_table) { qCritical() << QString("No [download] section found on mod metadata!"); diff --git a/launcher/modplatform/packwiz/Packwiz.h b/launcher/modplatform/packwiz/Packwiz.h index 58b86484..3c99769c 100644 --- a/launcher/modplatform/packwiz/Packwiz.h +++ b/launcher/modplatform/packwiz/Packwiz.h @@ -24,6 +24,7 @@ #include #include +struct toml_table_t; class QDir; // Mod from launcher/minecraft/mod/Mod.h @@ -31,6 +32,11 @@ class Mod; namespace Packwiz { +auto getRealIndexName(QDir& index_dir, QString normalized_index_name, bool should_match = false) -> QString; + +auto stringEntry(toml_table_t* parent, const char* entry_name) -> QString; +auto intEntry(toml_table_t* parent, const char* entry_name) -> int; + class V1 { public: struct Mod {